Repository navigation
Commit 8882996
feat(cli): nullify deps analyze — CI-aware dependency malware-analysis workflow (#142)
* feat(cli): nullify deps analyze — CI-aware dependency malware-analysis workflow
Hand-written workflow command (not generated from OpenAPI) that wires
the Phase 5a/5b malware analyzer into customer CI pipelines via scpm's
/scpm/dependencies/analyze endpoint.
Components:
- internal/ci/ — extensible CI provider registry. Nine concrete
Providers: GitHubActions, GitLabCI, CircleCI, BitbucketPipelines,
Jenkins, AzureDevOps, GoogleCloudBuild, AWSCodeBuild, Local (last;
always matches). Each exposes Platform() (matches the
benchmarks.PipelinePlatform enum), Detect() (env-var signature),
BaseRef / HeadRef (context-aware; fall back to
`git rev-parse origin/HEAD` when CI doesn't expose a base),
PRNumber, RepoSlug, and EnrichHeader (stamps CI metadata on outbound
HTTP so scpm's audit log can tie back to the CI run).
- internal/scan/manifest/ — per-ecosystem lockfile parsers:
npm (package-lock.json v2/v3), pypi (requirements.txt), go (go.sum),
rust (Cargo.lock), ruby (Gemfile.lock). Each implements Parser and is
registered in manifest.DefaultParsers(). Deliberately tight:
pnpm-lock, poetry.lock, pom.xml, composer.lock, packages.lock.json
are follow-up work. Every parser produces the same flat Entry
(Ecosystem, Name, Version, File, Direct) shape so diff.go can
compare them uniformly.
- internal/scan/diff.go — the (base, head) diff. Uses
`git diff --name-only base..head` to list changed files, filters to
those a parser matches, then `git show <ref>:<path>` on each side +
parse + set-difference. Produces ChangedDep{added, bumped, removed}.
- internal/commands/deps_analyze.go — the cobra command. Flow:
1. Detect CI provider; resolve base (--base or provider.BaseRef)
and head (--head or provider.HeadRef) to commit SHAs.
2. scan.Diff → list of changed deps.
3. For each added/bumped dep, POST to
{client.BaseURL}/scpm/dependencies/analyze with the CI-provider
headers + an idempotency key derived from CI run ID + package
tuple (so re-runs of the same CI build hit cached jobs).
4. Render results as text (default) or json.
5. Exit per --fail-on (none|vulnerable|suspicious|malicious):
0=clean, 10=vulnerable, 20=suspicious, 30=malicious, 2=invalid
invocation, 1=transient.
The scpm calls are hand-rolled HTTP POSTs rather than generated
api.Client methods because scpm's OpenAPI regen happens in a separate
pipeline — the CLI's scripts/generate/main.go will supersede these
once the spec lands. Follows the RegisterContextPushCommand pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style(cli): gofmt doc-comment blocks in CI provider files
golangci-lint v2.12.0 gofmt flagged the space-indented env-var tables in
the AWS CodeBuild / Azure DevOps / Bitbucket provider doc comments; reflow
to the Go 1.19 preformatted form (blank // + tab indent).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style(cli): gofmt remaining deps-analyze files
Reflow the rest of the CI-provider doc-comment tables to Go 1.19
preformatted form, and fix const/var/struct-tag alignment in
deps_analyze.go and npm_lock.go. golangci-lint caps gofmt reports at 3 per
run, so these surfaced only after the first batch was fixed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): make deps-analyze gate functional + add tests
Addresses review of the deps-analyze workflow.
Blockers:
- Wire ExitCodeFromError in main.go so the severity exit codes
(10/20/30) actually fire — previously every result exited 1, so the
CI merge-gate was inert. Set SilenceUsage so a verdict failure doesn't
print cobra usage.
- Fix --fail-on=none, which always exited 10: checkFailOn now treats a
zero-rank threshold as "never fail".
Correctness:
- renderResults no longer index-zips two parallel slices (a mid-list
analyze failure misattributed verdicts to the wrong package and
dropped the tail). One analyzedDep{dep,resp,err} entry per dep now.
- Unknown/unrecognised non-empty verdicts fail closed: warn and treat as
malicious so a server-side rename can't silently bypass the gate.
- Provider BaseRef/HeadRef + resolveRef take repoPath and set cmd.Dir,
so --repo is honoured (ref resolution no longer runs in the CWD).
Feature:
- Implement --wait (was parsed but unused): poll by re-POSTing the
idempotent analyze request until the job is terminal or --wait-timeout
elapses; adds --wait-timeout/--wait-interval. An incomplete analysis
now exits transiently (1) instead of greenlighting the merge.
Cleanup:
- Typed enums replace magic strings: ci.Platform, scan.ChangeKind,
manifest.Ecosystem, commands.Verdict.
- Remove the `var _ = fmt.Sprintf` hack and the //nolint:errcheck.
- Correct the doc comments that claimed a benchmarks.PipelinePlatform
enum and a `make lint-ci-providers` target that don't exist.
- Fix the misleading idempotency-key comment; handle SSH repo URLs in
AWSCodeBuild.RepoSlug; note fetch-depth:0 for GitHub push diffs.
Tests:
- Add unit tests for the five lockfile parsers, diffEntries, CI provider
detection/parsing, and the verdict/gate/render/wait/exit-code logic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent c740b98 commit 8882996
25 files changed
Lines changed: 2788 additions & 1 deletion
File tree
- cmd/cli
- cmd
- internal
- ci
- commands
- scan
- manifest
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
107 | 112 | | |
108 | 113 | | |
109 | 114 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
10 | 14 | | |
11 | | - | |
| 15 | + | |
12 | 16 | | |
13 | 17 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
0 commit comments