snyk-docker-plugin
Version:
Snyk CLI docker plugin
160 lines (123 loc) • 7.37 kB
Markdown
# AGENTS.md
Guidance for AI coding agents (Claude Code, Cursor, Copilot, etc.) working in this repo.
## What this is
`snyk-docker-plugin` is a library that extracts dependency metadata from
container images. It is consumed as a library — there is no CLI entry point of
its own. The public surface is exported from `lib/index.ts` (`scan`, `display`,
`extractContent`, `dockerFile`, plus the supporting types).
Known consumers include:
- `snyk/cli` — scan handler
- `snyk/kubernetes-monitor` — scan handler
- `snyk/container-image-collector` — scan handler (with `--exclude-app-vulns`)
- `snyk/docker-registry-agent` — scan handler
- `snyk/docker-deps` — types, Dockerfile/image analysis
- `snyk/kubernetes-upstream`, `snyk/kubernetes-agent`, `snyk/registry` — types only
Treat any change to `PluginResponse`, `ScanResult`, `Fact`, or `FactType` (and
the dockerfile-analysis types) as a public-API change with multiple downstream
consumers, not just the CLI.
## Output model: `PluginResponse` and Facts
`scan()` returns a `PluginResponse` containing one or more `ScanResult`s. Each
`ScanResult` carries a list of typed `Fact` objects — this is the contract with
every downstream consumer (see "What this is" above). All `FactType` values are
enumerated in `lib/types.ts`; concrete shapes live in `lib/facts.ts`. Common ones:
- `depGraph` — a `@snyk/dep-graph` for a package manager or application
- `dockerfileAnalysis` — base image, instructions, layers
- `imageLayers`, `imageId`, `imageNames`, `imageOsReleasePrettyName`,
`imageSizeBytes`, `imageCreationTime`, `imageLabels`
- `jarFingerprints`, `keyBinariesHashes` — for things not installed by a
package manager
- `imageManifestFiles` — raw manifest contents (e.g. `requirements.txt`)
- `pluginVersion`, `pluginWarnings`
When adding a new ecosystem or signal, emit a `Fact` with an existing
`FactType` if one fits; introducing a new `FactType` is a contract change
affecting every consumer and should be flagged in the PR.
## Repo layout
```
lib/
scan.ts Top-level entry: scan(options) -> PluginResponse
index.ts Public exports
extractor/ Read images: docker-archive, oci-archive, kaniko-archive
analyzer/ Identify OS, package managers, applications, runtimes
inputs/ Per-ecosystem file readers (apk, apt, rpm, node, java, python, php, binaries, ...)
parser/ Parse package manager databases into dep graphs
dependency-tree/ Dep graph construction helpers
dockerfile/ Dockerfile parsing and base-image analysis
go-parser/ Go binary parsing
python-parser/ Python package parsing
test/
unit/ lib/ Fast tests, no Docker required
system/ Integration tests; require a running Docker daemon + auth env vars
fixtures/ Image archives and sample data
```
## Setup
- Node `>=20.19` (see `.nvmrc`: `20`).
- `npm install` against the public npm registry. The `@snyk/*` runtime
dependencies are published publicly — no auth needed for local install.
(CI writes an `NPM_TOKEN` to `.npmrc`; you don't need to replicate that.)
- `npm run build` compiles TypeScript to `dist/`.
## Commands
Use these exact scripts — don't invent new ones.
| Task | Command |
| -------------------- | --------------------------------------------------- |
| Build | `npm run build` |
| Lint (all) | `npm run lint` |
| Auto-format + fix | `npm run format` |
| Unit tests (default) | `npm run test:unit` |
| System tests | `npm run test:system` (requires Docker — see below) |
| All tests | `npm test` |
For a quick inner loop, `npm run test:unit` is fastest. Run
`npm run test:system` (or full `npm test`) before declaring a change done,
provided Docker and the required env vars are available — see below. If they
aren't, say so explicitly rather than skipping silently.
## Testing rules
- **New tests must be Jest, with the `.spec.ts` suffix.** Files ending in
`.test.ts` are legacy `tap` tests — do not add new ones, and prefer migrating
rather than extending them.
- Jest config: `jest.config.js` (root) and `test/windows/jest.config.js`.
- System tests need:
- A running Docker daemon (with "Use containerd for pulling and storing
images" **disabled** in Docker Desktop — containerd causes SHA mismatches).
- Env vars `DOCKER_HUB_PRIVATE_IMAGE`, `DOCKER_HUB_USERNAME`,
`DOCKER_HUB_PASSWORD` (values in 1Password).
- At runtime, the plugin itself reads `SNYK_REGISTRY_USERNAME` /
`SNYK_REGISTRY_PASSWORD` — these are separate from the test creds.
- See `test/README.md` for the authoritative details.
### Snapshots
Some tests use Jest snapshots (`__snapshots__/` directories). Update with
`npx jest -u <pattern>` and **review the diff** — snapshot churn often hides
real behavior changes. Note: `jest.config.js` pins a custom `snapshotFormat`
to keep pre-Jest-29 snapshots readable; don't change it casually.
## Debugging
- The library uses the [`debug`](https://www.npmjs.com/package/debug) package.
Set `DEBUG=snyk-docker-plugin*` (or a more specific namespace) when running
tests or a consumer to see internal logs.
- `npm run debug` runs `tsc-watch` with `node --inspect --inspect-brk` for
step-through debugging in an attached debugger.
## CI
CircleCI (`.circleci/config.yml`) runs build, lint, and tests on:
- Linux (`cimg/node:20.19`) — full Jest suite, including system tests
- Windows (`win/server-2022`) — `test/windows/` suite via `npm run test-jest-windows`
`main`-branch failures notify Slack `#team-container-pipeline-info`. Match the
target Node major (`20`) when validating locally.
## Commit & PR conventions
- **Conventional commits, enforced by commitlint** (`@commitlint/config-conventional`).
Allowed types: `feat`, `fix`, `docs`, `test`, `chore`, `refactor`, `style`, `perf`.
Header max length 100. Example: `fix: handle empty layer in OCI archive`.
- For a bug fix, prefer committing a **failing test first**, then the fix in a
separate commit (see `.github/CONTRIBUTING.md`).
- CODEOWNERS: `@snyk/container_container` reviews everything by default.
## Things not to touch
- `dist/` — generated by `tsc`, gitignored, never hand-edit.
- Test fixtures under `test/fixtures/` are large and load-bearing — don't
regenerate or "tidy" them without a clear reason.
## Style
- TypeScript, formatted by Prettier (`{ "trailingComma": "all", "arrowParens": "always" }`)
and linted by tslint. Run `npm run format` before sending changes.
- Prefer editing existing files in `lib/<area>/` over creating new top-level
modules. Mirror the existing per-ecosystem layout (`inputs/<eco>`,
`analyzer/applications/<eco>`, `parser/<eco>`).
## When in doubt
- Public API: read `lib/index.ts` and `lib/types.ts`.
- How a scan flows end-to-end: start at `lib/scan.ts`.
- How to add support for a new ecosystem: look at an existing one under
`lib/inputs/` + `lib/analyzer/applications/` + `lib/parser/` as a template.