Skip to content

feat(security): scan connected repositories' lockfiles for vulnerable dependencies - #2044

Open
Makisuo wants to merge 14 commits into
feat/depscan-3-storefrom
feat/depscan-4-scanner
Open

Makisuo wants to merge 14 commits into
feat/depscan-3-storefrom
feat/depscan-4-scanner

fix(security): spend the findings row limit on connected repositories

f416695
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review succeeded Nov 1, 2026 in 2m 8s

Confidence 9/10 · No issues found

🟢 Confidence 9/10 · safe to merge. Scanner, store and queue triggers are well pinned; the new /internal/dependency-scan handlers, including the admin gate, have no test, and the push dedupe reads only lockfile shas.
1 note · tests partial −1 · risk medium · 4/4 new units observable

Adds a dependency scanner that reads connected repositories' lockfiles through the existing GitHub App, runs it from the VCS sync queue on tracked-branch pushes, installation syncs and "Scan now", and serves findings and admin-only triage over a new internal API. Neither open error issue is fixed: GithubAppError gains more call sites here, and DeviceTokenRejected is untouched by the route registration in http-graph.ts.

  • DependencyScanner.scan reads lockfiles by GitHub blob sha and matches packages in OSV
  • VCS sync enqueues dependency-scan jobs on tracked-branch pushes and installation syncs
  • A push scan is skipped when the last scan read the same lockfile shas
  • /internal/dependency-scan serves repositories, findings and admin-only triage

Findings

🔵 Note · F6 · Push dedupe ignores package.json, so yarn findings keep a stale relation

correctness · packages/backend/src/services/dependency-scan/DependencyScanner.ts:275

sameLockfiles compares only lockfile paths and blob shas, yet a yarn scan also reads package.json (line 144) to classify direct/dev. A commit that only moves a dependency between dependencies and devDependencies, or adds a workspace manifest, returns Unchanged, so findings keep the old relation until yarn.lock itself changes.

Include the scan's manifest blob shas in the unchanged check (store them on the scan row), or skip the dedupe when `selected` holds a yarn lockfile.

Fixed since the last review

  • ✅ F5 · Removed repositories' findings still occupy the 500-row read before they are dropped

Production impact

Open errors in the changed files (2)
Issue Service Occurrences File
@maple/backend/vcs/GithubAppError maple-api 24 packages/backend/src/services/integrations/vcs/VcsSyncService.ts
@maple/backend/auth/device/DeviceTokenRejected maple-api 3 apps/api/src/runtime/http-graph.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/integrations/vcs/VcsSyncService.ts 8.4k VcsSyncService.resolveRepositoryForJob 8.2k/day, 0.0% err
VcsSyncService.handleBranchEvent 218/day, 0.0% err
Telemetry this change adds and removes (17)
  • ➕ span name DependencyScanService.connectedRepositories · packages/backend/src/services/dependency-scan/DependencyScanService.ts:114
  • ➕ span name DependencyScanner.readLockfile · packages/backend/src/services/dependency-scan/DependencyScanner.ts:196
  • ➕ attribute maple.depscan.lockfile.path · packages/backend/src/services/dependency-scan/DependencyScanner.ts:198
  • ➕ attribute maple.depscan.lockfile.kind · packages/backend/src/services/dependency-scan/DependencyScanner.ts:199
  • ➕ span name DependencyScanner.scan · packages/backend/src/services/dependency-scan/DependencyScanner.ts:226
  • ➕ attribute maple.depscan.trigger · packages/backend/src/services/dependency-scan/DependencyScanner.ts:238
  • ➕ attribute maple.depscan.lockfile_count · packages/backend/src/services/dependency-scan/DependencyScanner.ts:261
  • ➕ attribute maple.depscan.lockfiles_skipped · packages/backend/src/services/dependency-scan/DependencyScanner.ts:262
  • ➕ attribute maple.depscan.tree_truncated · packages/backend/src/services/dependency-scan/DependencyScanner.ts:263
  • ➕ attribute maple.depscan.outcome · packages/backend/src/services/dependency-scan/DependencyScanner.ts:277
  • ➕ attribute maple.depscan.scan_id · packages/backend/src/services/dependency-scan/DependencyScanner.ts:292
  • ➕ attribute maple.depscan.package_count · packages/backend/src/services/dependency-scan/DependencyScanner.ts:315
  • ➕ attribute maple.depscan.match_count · packages/backend/src/services/dependency-scan/DependencyScanner.ts:316
  • ➕ span name VcsSyncService.handleDependencyScan · packages/backend/src/services/integrations/vcs/VcsSyncService.ts:858
  • ➕ attribute maple.depscan.forwarded · packages/backend/src/services/integrations/vcs/VcsSyncService.ts:865
  • ➕ span name GithubAppClient.getRecursiveTree · packages/backend/src/services/integrations/vcs/vendor/github/GithubAppClient.ts:2165
  • ➕ span name GithubAppClient.getBlob · packages/backend/src/services/integrations/vcs/vendor/github/GithubAppClient.ts:2187
What was checked
  • manifestsFor/sameLockfiles and the go.sum, node_modules exclusions in lockfile-selection.ts:47-73
  • Scan pushes enqueue only after the tracked-branch gate in applyPush (VcsSyncService.ts:425)
  • Findings reads are org- and repository-scoped in SQL, so a removed repository's rows cannot surface
Observability coverage: 4 of 4 changes observable
Change Kind Observable Evidence
Dependency scan of one repository (queue job) background work yes DependencyScanner.scan span with maple.depscan.trigger, vcs.commit.sha, maple.depscan.outcome; VcsSyncService.handleDependencyScan span
GitHub recursive tree and blob reads outbound HTTP yes GithubAppClient.getRecursiveTree/getBlob spans over the existing authedGet client
/internal/dependency-scan endpoints inbound HTTP yes Served by the API HTTP graph; handlers annotate orgId, maple.depscan.repository_id, result.rowCount
Failed scan error path yes failScan records the failure on the row; [DependencyScan] scan failed structured log carries the summarized cause

f416695 · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

Annotations

Check notice on line 275 in packages/backend/src/services/dependency-scan/DependencyScanner.ts

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

correctness: Push dedupe ignores `package.json`, so yarn findings keep a stale `relation`

`sameLockfiles` compares only lockfile paths and blob shas, yet a yarn scan also reads `package.json` (line 144) to classify direct/dev. A commit that only moves a dependency between `dependencies` and `devDependencies`, or adds a workspace manifest, returns `Unchanged`, so findings keep the old `relation` until `yarn.lock` itself changes.