enrich-googleosi Design
Enriches package records from the Google OSI (deps.dev) API for packages referenced in CVE affected data, and for the registry packages the ecosystem processors ingested that no CVE has named yet.
Overview
- Batch size: 1 package per run
- Schedule: Every 15 minutes (
cron(*/15 * * * ? *)), 256 CPU / 512 MB,EXPECTED_DURATION_MINUTES=45 - Concurrency: No master lock — the per-package tracker row is claimed before any fetch, so two overlapping runs cannot pick the same package
- State: Per-package rows in
BulkDataDumpTracker, keyedenrich_googleosi:{SYSTEM}:{packageName}(e.g.enrich_googleosi:NPM:lodash). Legacyenrich_googleosi:{cveId}rows from an earlier CVE-driven design are skipped as malformed. - Throughput ceiling: 96 packages/day. Phase 1 (the CVE-driven queue) keeps up; phase 1b is a very long tail — see Queue phases.
Queue phases
findNextPackageBatch tries three sources in strict priority order and returns
on the first that yields anything. Each phase runs on its own
context.Background() 5-minute timeout, so a near-expired task context cannot
cancel a scan mid-flight.
| Phase | Query | Rationale |
|---|---|---|
| 1 | DISTINCT (collectionURL, packageName) from CVEAffected for a supported registry, with no tracker row | The original behaviour: packages already known to be vulnerable, enriched first |
| 1b | PackageVersion rows whose sourceRepoUrl IS NULL and which have no tracker row, ordered discoveredAt DESC | Phase 1 only ever sees packages a CVE already named. The registry processors ingest millions of others into PackageVersion and none of them was ever asked about — and a package’s provenance is most useful before anyone finds a vulnerability in it. Ordered newest-first so the query terminates early against the discoveredAt index instead of scanning the whole table. |
| 2 | Oldest tracker rows in the enrich_googleosi: key range, ORDER BY lastProcessedAt ASC | Refresh pass. Uses explicit >= lo AND < hi range bounds rather than LIKE, so the composite (lastProcessedAt, source) index is reliably chosen with runtime parameters. |
sourceRepoUrl IS NULL is phase 1b’s not-yet-enriched marker because it is the
field this enrichment reliably sets. Note that db.SetPackageVersionTEAFields is
an UPDATE keyed on (ecosystem, packageName, version) — if deps.dev reports
versions that do not exist as PackageVersion rows, nothing is written and
sourceRepoUrl stays null. The tracker row still exists, so the package is not
re-selected; it is simply not improvable from deps.dev.
Processing Flow
- Find the next package via the three phases above
- Claim it (upsert the per-package tracker row) before fetching, so a crash mid-fetch does not re-serve it
FetchDepsDevPackage: GEThttps://api.deps.dev/v3/systems/{SYSTEM}/packages/{packageName}— the host isapi.deps.dev, notdeps.dev/api, which serves HTML- For each version of the package:
walkDependencyTree(depth=0, maxDepth=3)- Fetch version info → upsert
Dependency,DependencyLink,DependencyRegistry,DependencySLSAProvenance,DependencyAttestation - Write back to
PackageVersion(writePackageVersionTEAFields):publishedAtandsourceRepoUrlviadb.SetPackageVersionTEAFields. Every argument is optional and nil leaves the stored value alone, so an enrichment pass can add to a package record but never erode one. deps.dev carries no licence expression on the version endpoint, solicenseExpressionis always passed nil. - If a
SOURCE_REPOorREPOSITORYlink points to github.com (first match only):CreateOrUpdateGitHubRepository→ upsertGitHubRepositoryUpsertGitHubBranches,UpsertGitHubContributors,UpsertGitHubLanguagesUpsertOpenSSFScorecardviaapi.scorecard.devUpsertGitHubRepoPackageManager→UpsertGitHubRepoDependency
- Fetch the dependency graph → recurse on every non-
SELFnode. Cycle prevention uses an in-memory visited set plus a persistentBulkDataDumpTrackercheck that skips any(SYSTEM:name:version)enriched within the last 12 hours.
- Fetch version info → upsert
MarkPackageCVEsEnriched: updateCVEMetadata.lastEnrichedfor every CVE whoseCVEAffectedrow names this package on one of the system’s collection URLs
FetchDepsDevAdvisory / EnrichCVEWithDepsDevData still exist in
internal/enrichment/googleosi.go but are not on this path — no
CVEMetadataReferences rows with referenceSource=GOOGLE_OSI are produced.
Ecosystem ↔ system mapping
deps.dev, the registry processors, and the GitHub package-manager vocabulary all
spell ecosystems differently (PYPI / pypi / pip). Matching on the wrong one
silently updates nothing, which is indistinguishable from a package with no extra
data, so the mapping is explicit in depsDevSystemToEcosystem and exported once
via EcosystemSystemPairs() for the phase-1b SQL: NPM, PYPI, GO, MAVEN, CARGO,
RUBYGEMS, NUGET, PACKAGIST, PUB, HEX.
orgId Resolution
GitHubRepoDependency.orgId and GitHubRepoPackageManager.orgId are required
String fields. For VDB enrichment, orgId = "CNA-2019-0009" is a constant
(internal/enrichment/github_enrichment.go), and the matching
CVENumberingAuthority row is upserted on demand — no manual seed is required.
Tables Written
PackageVersion—publishedAt,sourceRepoUrl,lastVerifiedAton the row the ecosystem processors own (UPDATE only, COALESCE’d — never inserts, never blanks)Dependency— package from deps.dev version infoDependencyLink— HOMEPAGE/SOURCE_REPO/REPOSITORY linksDependencyRegistry— registry URLsDependencySLSAProvenance— SLSA provenance from deps.devDependencyAttestation— attestation recordsGitHubRepository— GitHub repo metadata (if SOURCE_REPO/REPOSITORY link found)GitHubBranch— branches via GitHub APIGitHubRepoContributor— contributors via GitHub APIGitHubRepoLanguage— language breakdown via GitHub APIGitHubRepoPackageManager— package manager record (orgId=CNA-2019-0009)GitHubRepoDependency— repo↔dependency link (orgId=CNA-2019-0009)OpenSSFScorecard— scorecard from api.scorecard.devCVENumberingAuthority— the('CNA-2019-0009','GitHub')seed row, upserted on demandCVEMetadata.lastEnriched— enrichment timestampBulkDataDumpTracker— per-package state + the 12-hour per-(SYSTEM:name:version)dedup markers
Environment Variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | Write connection |
DATABASE_URL_READ | No | Read replica |
GITHUB_PAT | No | Token for the GitHub repo / branch / contributor / language lookups. Unset means those calls run unauthenticated and rate-limit quickly. Supplied in ECS via local.go_task_secrets_github. |
S3 Persistence
- Archive: ⚠ Not yet implemented — requires record reconstruction (DB row → canonical JSON).
- Quarantine: ⚠ Not yet implemented — same reason.
- Likely reasons when implemented:
enrich-error
This is an enrichment processor; it reads from CVEMetadata rather than ingesting raw feeds, so there is no original payload to archive verbatim. See S3 Persistence Contract § Processors whose unit-of-work is not a file.