diff --git a/.cursor/rules/docs-ux.mdc b/.cursor/rules/docs-ux.mdc index 0bc5f2a..d74a1cc 100644 --- a/.cursor/rules/docs-ux.mdc +++ b/.cursor/rules/docs-ux.mdc @@ -19,3 +19,5 @@ You own: All user-facing documentation in `docs/` (getting started, CLI referenc - Inventing flags or behavior not implemented in the CLI (Scanner Core). - Long duplicate pipeline YAML in prose; use snippets from `ci/` and link. + + diff --git a/.cursor/rules/documentation-agent.mdc b/.cursor/rules/documentation-agent.mdc index 92b60f5..88922df 100644 --- a/.cursor/rules/documentation-agent.mdc +++ b/.cursor/rules/documentation-agent.mdc @@ -46,3 +46,5 @@ Use `docs/DOCUMENTATION-AGENT.md` as the single source of truth. For each change - Do not update `docs/LESSONS-LEARNED.md` (owned by Lessons Learned Agent). - Do not invent flags or behavior that are not in the code. - Do not leave docs out of date after a feature is merged or a task is completed. + + diff --git a/.cursor/rules/enterprise-ops.mdc b/.cursor/rules/enterprise-ops.mdc index 3980935..c022438 100644 --- a/.cursor/rules/enterprise-ops.mdc +++ b/.cursor/rules/enterprise-ops.mdc @@ -19,3 +19,5 @@ You own: Policy (severity thresholds, `--fail-on`, fail-on-count), config file s - Putting secrets in config file or logs. Use env vars for registry auth and sensitive data. - Changing CLI flags or scan behavior in a way that breaks existing pipelines; prefer additive changes and document deprecations. + + diff --git a/.cursor/rules/integrations.mdc b/.cursor/rules/integrations.mdc index dba4aaa..3983564 100644 --- a/.cursor/rules/integrations.mdc +++ b/.cursor/rules/integrations.mdc @@ -19,3 +19,5 @@ You own: Pipeline templates and snippets in `ci/` (Azure, GitHub, GitLab, Jenkin - Changing CLI flags or behavior; that is Scanner Core. If the CLI contract changes, update the snippets and mention it in the integration guide. - Duplicating long prose that belongs in the main docs (e.g. "What is SARIF?"); link to `docs/` instead. + + diff --git a/.cursor/rules/lessons-learned-agent.mdc b/.cursor/rules/lessons-learned-agent.mdc index 240a729..c249ec2 100644 --- a/.cursor/rules/lessons-learned-agent.mdc +++ b/.cursor/rules/lessons-learned-agent.mdc @@ -38,3 +38,5 @@ Keep each entry short (under 10 lines). The file is for review “once all is sa - Do not remove or rewrite existing entries (only append). - Do not use this file for general documentation (use Documentation Agent docs). - Do not add entries for trivial edits (e.g. typos); only for meaningful tasks or decisions. + + diff --git a/.cursor/rules/scanner-core.mdc b/.cursor/rules/scanner-core.mdc index 0daa6fe..6fa6425 100644 --- a/.cursor/rules/scanner-core.mdc +++ b/.cursor/rules/scanner-core.mdc @@ -22,3 +22,5 @@ You own: CLI (`cmd/cli`), scanner engine (`pkg/scanner`), remediation enricher ( - Duplicating scan or remediation logic elsewhere (e.g. in server or UI). Server/UI call CLI or shared packages. - Putting secrets in logs or report output. - Changing the finding model without updating enricher and report in the same change. + + diff --git a/.cursor/rules/web-frontend.mdc b/.cursor/rules/web-frontend.mdc index 0883522..470f522 100644 --- a/.cursor/rules/web-frontend.mdc +++ b/.cursor/rules/web-frontend.mdc @@ -19,3 +19,5 @@ You own: Optional scanner server (`cmd/server`) and Web UI (`web/`): serve UI, e - Implementing a second scanner or report generator; consume the same finding model and report generator as the CLI. - Adding UI-only options that have no CLI equivalent without coordinating with Scanner Core (for consistency). + + diff --git a/.gitignore b/.gitignore index 7ffb5d4..fae6c72 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ test-results/ # OS .DS_Store Thumbs.db + +# renovated: 2026-07-02 diff --git a/Dockerfile b/Dockerfile index 8f8d3a3..f092510 100644 --- a/Dockerfile +++ b/Dockerfile @@ -69,3 +69,5 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ # ───────────────────────────────────────────────────────────────────────────── ENTRYPOINT ["scanner"] CMD ["scan", "--help"] + +# renovated: 2026-07-02 diff --git a/Makefile b/Makefile index 853d325..7e653ad 100644 --- a/Makefile +++ b/Makefile @@ -93,3 +93,5 @@ docker-serve-stop: .PHONY: build build-server build-all scan deps serve \ test-unit test-integration test setup-and-test \ docker-build docker-scan docker-serve docker-serve-bg docker-serve-stop + +# renovated: 2026-07-02 diff --git a/cmd/baseline/main.go b/cmd/baseline/main.go index 250aaa6..d17ca8f 100644 --- a/cmd/baseline/main.go +++ b/cmd/baseline/main.go @@ -18,8 +18,8 @@ import ( "sync" "time" - "github.com/docker-scanner/scanner/pkg/report" "github.com/docker-scanner/scanner/pkg/remediate" + "github.com/docker-scanner/scanner/pkg/report" "github.com/docker-scanner/scanner/pkg/scanner" ) @@ -105,10 +105,10 @@ func main() { } var ( - mu sync.Mutex - results []result - allFindings []report.ImageFinding - work = make(chan string, len(images)) + mu sync.Mutex + results []result + allFindings []report.ImageFinding + work = make(chan string, len(images)) ) for _, img := range images { @@ -361,17 +361,17 @@ func loadImages(path string) ([]string, error) { // dashboardData is embedded in HTML for Chart.js; all labels use image name/path. type dashboardData struct { - Images []imageRow `json:"images"` // image name/path, findings count, duration_sec, status - SeverityCounts map[string]int `json:"severity"` // CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN + Images []imageRow `json:"images"` // image name/path, findings count, duration_sec, status + SeverityCounts map[string]int `json:"severity"` // CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN ExploitableCounts map[string]int `json:"exploitable"` // yes, no, unknown - ReportTime string `json:"reportTime"` + ReportTime string `json:"reportTime"` } type imageRow struct { - Image string `json:"image"` - Findings int `json:"findings"` + Image string `json:"image"` + Findings int `json:"findings"` DurationSec float64 `json:"duration_sec"` - Status string `json:"status"` + Status string `json:"status"` } func writeDashboardHTML(results []result, allFindings []report.ImageFinding, outPath, reportTime string) error { @@ -379,10 +379,10 @@ func writeDashboardHTML(results []result, allFindings []report.ImageFinding, out imgMap := make(map[string]*imageRow) for _, r := range results { imgMap[r.Image] = &imageRow{ - Image: r.Image, - Findings: r.Findings, + Image: r.Image, + Findings: r.Findings, DurationSec: r.Duration.Seconds(), - Status: r.Status, + Status: r.Status, } } var images []imageRow diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 16a7ed5..a43c04d 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -35,9 +35,9 @@ func main() { timestamp := scanCmd.Bool("timestamp", false, "Append timestamp to report base name so each run writes unique files (e.g. report-20060102-150405.html).") format := scanCmd.String("format", "sarif,markdown", "Comma-separated formats: sarif, markdown, html, csv") failOnSeverity := scanCmd.String("fail-on-severity", "", "Exit with code 1 if any finding has this severity (e.g. CRITICAL,HIGH). Empty = do not fail.") - failOnCount := scanCmd.String("fail-on-count", "", "Exit with code 1 if count for severity >= N (e.g. HIGH:5). One rule only.") - checkRuntime := scanCmd.Bool("check-runtime", false, "Check host runc version for known container escape CVEs (requires docker or runc in PATH).") - sbom := scanCmd.Bool("sbom", false, "Generate a CycloneDX SBOM alongside the vulnerability report (image scans only).") + failOnCount := scanCmd.String("fail-on-count", "", "Exit with code 1 if count for severity >= N (e.g. HIGH:5). One rule only.") + checkRuntime := scanCmd.Bool("check-runtime", false, "Check host runc version for known container escape CVEs (requires docker or runc in PATH).") + sbom := scanCmd.Bool("sbom", false, "Generate a CycloneDX SBOM alongside the vulnerability report (image scans only).") if len(os.Args) < 2 { fmt.Fprintln(os.Stderr, "Usage: scanner scan --image [options] (Docker/Podman image)") diff --git a/cmd/mcp-server/main.go b/cmd/mcp-server/main.go index 7c6968e..650f639 100644 --- a/cmd/mcp-server/main.go +++ b/cmd/mcp-server/main.go @@ -83,15 +83,15 @@ type ScanRootfsInput struct { } type ScanResult struct { - Ok bool `json:"ok"` - Target string `json:"target"` - Count int `json:"findings_count"` - Summary string `json:"summary"` - Exploitable int `json:"exploitable_count"` - Findings []scanFindingSummary `json:"findings,omitempty"` - ReportDir string `json:"report_dir,omitempty"` - PolicyFail bool `json:"policy_violated,omitempty"` - Error string `json:"error,omitempty"` + Ok bool `json:"ok"` + Target string `json:"target"` + Count int `json:"findings_count"` + Summary string `json:"summary"` + Exploitable int `json:"exploitable_count"` + Findings []scanFindingSummary `json:"findings,omitempty"` + ReportDir string `json:"report_dir,omitempty"` + PolicyFail bool `json:"policy_violated,omitempty"` + Error string `json:"error,omitempty"` } type scanFindingSummary struct { diff --git a/cmd/server/main.go b/cmd/server/main.go index a224877..905667c 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -2,7 +2,8 @@ // Serves the frontend at / and exposes a streaming SSE scan endpoint at /api/scan. // // Usage: go run ./cmd/server (default: port 8080) -// go run ./cmd/server -port 9090 +// +// go run ./cmd/server -port 9090 package main import ( @@ -79,11 +80,11 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { // sseEvent is the JSON payload sent over SSE. type sseEvent struct { - Type string `json:"type"` // "status" | "complete" | "error" - Message string `json:"message,omitempty"` // status text shown in UI + Type string `json:"type"` // "status" | "complete" | "error" + Message string `json:"message,omitempty"` // status text shown in UI Findings []scanner.Finding `json:"findings,omitempty"` // only on "complete" - Summary *scanSummary `json:"summary,omitempty"` // only on "complete" - Elapsed string `json:"elapsed,omitempty"` // human duration, only on "complete" + Summary *scanSummary `json:"summary,omitempty"` // only on "complete" + Elapsed string `json:"elapsed,omitempty"` // human duration, only on "complete" } type scanSummary struct { diff --git a/docker-compose.yml b/docker-compose.yml index 8a5645e..03abef8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -75,3 +75,5 @@ services: volumes: trivy-cache: driver: local + +# renovated: 2026-07-02 diff --git a/docs/DOCUMENTATION-AGENT.md b/docs/DOCUMENTATION-AGENT.md index 261fbed..9d8167e 100644 --- a/docs/DOCUMENTATION-AGENT.md +++ b/docs/DOCUMENTATION-AGENT.md @@ -50,3 +50,5 @@ This file is the **single source of truth** for the Documentation Agent. When an ## Agent instruction **Documentation Agent:** The Cursor rule `documentation-agent.mdc` is **alwaysApply: true**, so the agent runs in context after any change. After any task that changes code, CLI, config, or behavior (including completion of a roadmap item), run through this checklist. Update every doc that is triggered (see table and triggers above). Keep Help and user-facing prose layman-friendly. Do not modify `docs/LESSONS-LEARNED.md` content (that is owned by the Lessons Learned Agent). The coder does not need to remember to update docs; the agent is responsible. + + diff --git a/docs/HELP.md b/docs/HELP.md index 791fd63..6f3bb3e 100644 --- a/docs/HELP.md +++ b/docs/HELP.md @@ -29,7 +29,7 @@ You can scan **container images** (Docker, Podman, containerd) with `--image diff --git a/docs/cli-reference.md b/docs/cli-reference.md index fc366f9..13567ed 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -83,3 +83,5 @@ scanner db update [--cache-dir ] - Registry auth: use Docker login (`docker login `) or env vars supported by the Docker/containerd client. The scanner does not accept credentials via env; use the standard Docker config. - No secrets in config file or logs. + + diff --git a/docs/docker-scanner.code-workspace b/docs/docker-scanner.code-workspace index 2a0ed79..1d31f12 100644 --- a/docs/docker-scanner.code-workspace +++ b/docs/docker-scanner.code-workspace @@ -4,4 +4,5 @@ "path": ".." } ] -} \ No newline at end of file +} + diff --git a/docs/getting-started.md b/docs/getting-started.md index 3bdcd62..cdaf9c3 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -64,7 +64,7 @@ On Windows the binary will be `scanner.exe`. You can also use `go run ./cmd/cli` ``` On Windows: `.\scanner.exe scan --image alpine:latest --output-dir .\reports` -3. **Open the report** +3. **Open the report** Reports are written to the folder you gave (`./reports` or `/reports`). Open `report.md` (readable) or `report.html` in a browser. You also get `report.sarif` for Azure/GitHub Security. Reports include **Exploitable** (CISA KEV + OSV.dev), **Why severity**, and **Exploit info**; see [Vulnerability reports](vulnerability-reports.md). **Optional extras on your first scan:** diff --git a/docs/hardened-images-and-local-registries.md b/docs/hardened-images-and-local-registries.md index aa4bbf1..d93a51a 100644 --- a/docs/hardened-images-and-local-registries.md +++ b/docs/hardened-images-and-local-registries.md @@ -112,3 +112,5 @@ When you introduce a new registry (e.g. your company’s Harbor or local registr | What constitutes a hardened image? | Minimal base, fewer packages, non-root, distroless/signed where possible, regularly updated. You define your own bar and which refs go in the list. | | Can we scan microservices alongside Docker images? | Yes, by scanning **one image per microservice**. Add all microservice image refs to the baseline list; the report is per image (per microservice). | | Can we pull from local repos if devs give access? | Yes. Use `docker login `, then add the registry’s image refs to a list and run the scanner/baseline as usual. Document the registry in [Image sources](image-sources.md). | + + diff --git a/docs/ide-and-mcp.md b/docs/ide-and-mcp.md index e3cef95..4ca9ff9 100644 --- a/docs/ide-and-mcp.md +++ b/docs/ide-and-mcp.md @@ -254,3 +254,5 @@ See **ide/jetbrains/README.md** for more detail. | CLI / CI | Use **scanner** CLI or **baseline** as documented in [CLI reference](cli-reference.md) and [Baseline](baseline.md). | All of the above require the **scanner** (and for full scans, **Trivy**) to be available; see [Getting started](getting-started.md) and [Help](HELP.md). + + diff --git a/docs/image-sources.md b/docs/image-sources.md index 17111a2..e5cb70d 100644 --- a/docs/image-sources.md +++ b/docs/image-sources.md @@ -63,3 +63,5 @@ This keeps a single place to see where every image comes from and how to find mo - **Obscure list:** `images-obscure.txt` includes very old tags (e.g. Alpine 3.5, Debian jessie). Some may be removed by publishers over time; baseline will report FAIL for those. Replace or remove refs as needed. - **Auth:** For any registry, if pull is denied or forbidden, use `docker login ` (or CI secrets) where you have access. + + diff --git a/docs/system-design.md b/docs/system-design.md index b30cb1e..b0aa92d 100644 --- a/docs/system-design.md +++ b/docs/system-design.md @@ -66,3 +66,5 @@ Auth: Server can sit behind a reverse proxy (OAuth, SSO). CLI uses env vars or D --- *Update this doc when adding server, UI, or new deployment options. Link from README and CONTRIBUTING.* + + diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index d119634..ea70cc5 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -124,3 +124,5 @@ trivy image --format json - Use `--offline` when no network is available. The vulnerability DB must already be in the cache (run once with network, or use a pre-populated cache volume in CI). - In offline mode, remediation uses only Trivy data and embedded rules; no OSV or other network calls. + + diff --git a/docs/vulnerability-reports.md b/docs/vulnerability-reports.md index 434ad0f..2e006ef 100644 --- a/docs/vulnerability-reports.md +++ b/docs/vulnerability-reports.md @@ -59,3 +59,5 @@ In online mode, Trivy runs with `--detection-priority comprehensive`, falling ba - **Baseline run** (100+ images): use `go run ./cmd/baseline` to scan the full image list. The resulting CSV/Markdown shows **Findings** and **Duration (s)** per image so you can see which images or stacks have more issues and where the scanner might be slow or missing coverage. - **Exploitable = yes**: if your baseline report has many Critical/exploitable items, focus remediation there first; if you see none, that may indicate older or well-patched images—or a need to add more “real-world” images to the list. - **Missing CVEs**: the scanner relies on Trivy’s DB, CISA KEV, and OSV.dev. For exploit status beyond KEV, check NVD and vendor advisories (linked in the report). + + diff --git a/ide/jetbrains/README.md b/ide/jetbrains/README.md index 6ecc599..510d5c1 100644 --- a/ide/jetbrains/README.md +++ b/ide/jetbrains/README.md @@ -26,3 +26,5 @@ To use the scanner from an AI assistant (e.g. Cursor), run the MCP server and ad ## Publishing to the JetBrains Marketplace So the plugin appears when users search in **Settings → Plugins**: create a vendor on [JetBrains Marketplace](https://plugins.jetbrains.com/), build the plugin (Gradle → buildPlugin), then upload the ZIP from **Your account → Upload plugin**. Full steps: [IDE and MCP — Publishing the JetBrains plugin](../../docs/ide-and-mcp.md#publishing-the-jetbrains-plugin-to-the-marketplace). + + diff --git a/ide/vscode/README.md b/ide/vscode/README.md index ebd52b1..f921f3a 100644 --- a/ide/vscode/README.md +++ b/ide/vscode/README.md @@ -24,3 +24,5 @@ To use the scanner from an AI assistant (e.g. Cursor), run the MCP server and ad ## Publishing to the VS Code Marketplace So the extension appears when users search “Docker Scanner” in Extensions: create a publisher on the [VS Code Marketplace](https://marketplace.visualstudio.com/), set `publisher` in `package.json` to your publisher ID, then run `npm i -g @vscode/vsce` and `vsce publish` (with a Personal Access Token from Azure DevOps). Full step-by-step: [IDE and MCP — Making the extension show up in search](../../docs/ide-and-mcp.md#publishing-the-vs-code-extension-to-the-marketplace). + + diff --git a/ide/vscode/tsconfig.json b/ide/vscode/tsconfig.json index aeb2122..9f3f0ac 100644 --- a/ide/vscode/tsconfig.json +++ b/ide/vscode/tsconfig.json @@ -10,3 +10,4 @@ }, "exclude": ["node_modules", "out"] } + diff --git a/pkg/config/config.go b/pkg/config/config.go index 67a82de..a79a418 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -11,12 +11,12 @@ import ( // Config holds default values for scan (severity, format, output-dir, etc.). // Empty values mean "use CLI default". No secrets; use for paths and options only. type Config struct { - Severity string `yaml:"severity"` // Comma-separated: CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN - Format string `yaml:"format"` // Comma-separated: sarif, markdown, html, csv - OutputDir string `yaml:"output-dir"` // Report output directory - OutputName string `yaml:"output-name"` // Base name for report files - CacheDir string `yaml:"cache-dir"` // Trivy cache directory - FailOnSeverity string `yaml:"fail-on-severity"` // Comma-separated severities that cause exit 1 + Severity string `yaml:"severity"` // Comma-separated: CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN + Format string `yaml:"format"` // Comma-separated: sarif, markdown, html, csv + OutputDir string `yaml:"output-dir"` // Report output directory + OutputName string `yaml:"output-name"` // Base name for report files + CacheDir string `yaml:"cache-dir"` // Trivy cache directory + FailOnSeverity string `yaml:"fail-on-severity"` // Comma-separated severities that cause exit 1 FailOnCount string `yaml:"fail-on-count"` // SEVERITY:N (e.g. HIGH:5) } diff --git a/pkg/kev/kev.go b/pkg/kev/kev.go index 3c422c5..6bd807d 100644 --- a/pkg/kev/kev.go +++ b/pkg/kev/kev.go @@ -14,9 +14,9 @@ var cisaKEVURL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited type kevCatalog struct { Vulnerabilities []struct { CveID string `json:"cveID"` - ShortDescription string `json:"shortDescription"` + ShortDescription string `json:"shortDescription"` VulnerabilityName string `json:"vulnerabilityName"` - KnownRansomware string `json:"knownRansomwareCampaignUse"` + KnownRansomware string `json:"knownRansomwareCampaignUse"` } `json:"vulnerabilities"` } diff --git a/pkg/osv/osv.go b/pkg/osv/osv.go index c3516b9..984fdaf 100644 --- a/pkg/osv/osv.go +++ b/pkg/osv/osv.go @@ -84,10 +84,10 @@ type osvQueryResponse struct { } type osvVuln struct { - ID string `json:"id"` - Aliases []string `json:"aliases"` - Summary string `json:"summary"` - Severity []osvSev `json:"severity"` + ID string `json:"id"` + Aliases []string `json:"aliases"` + Summary string `json:"summary"` + Severity []osvSev `json:"severity"` } type osvSev struct { diff --git a/pkg/remediate/enrich_integration_test.go b/pkg/remediate/enrich_integration_test.go index 878a11d..9d11bfe 100644 --- a/pkg/remediate/enrich_integration_test.go +++ b/pkg/remediate/enrich_integration_test.go @@ -117,11 +117,13 @@ func TestEnrich_kevMissMarksNotExploitable(t *testing.T) { // TestEnrich_ransomwareMentionedInExploitInfo verifies ransomware flag flows through. func TestEnrich_ransomwareMentionedInExploitInfo(t *testing.T) { type vuln struct { - CveID string `json:"cveID"` + CveID string `json:"cveID"` ShortDescription string `json:"shortDescription"` KnownRansomware string `json:"knownRansomwareCampaignUse"` } - type catalog struct{ Vulnerabilities []vuln `json:"vulnerabilities"` } + type catalog struct { + Vulnerabilities []vuln `json:"vulnerabilities"` + } payload, _ := json.Marshal(catalog{Vulnerabilities: []vuln{ {CveID: "CVE-2023-9999", ShortDescription: "Ransom vuln", KnownRansomware: "Known"}, }}) diff --git a/pkg/remediate/enrich_test.go b/pkg/remediate/enrich_test.go index cfc037d..e55fbef 100644 --- a/pkg/remediate/enrich_test.go +++ b/pkg/remediate/enrich_test.go @@ -110,7 +110,7 @@ func TestEnrich(t *testing.T) { want: []scanner.Finding{ { CVEID: "CVE-2020-1234", Package: "openssl", Severity: "HIGH", - RemediationText: "Upgrade or patch openssl (currently ); no fixed version in DB", + RemediationText: "Upgrade or patch openssl (currently ); no fixed version in DB", RemediationLinks: []string{"https://nvd.nist.gov/vuln/detail/CVE-2020-1234", "https://avd.aquasec.com/nvd/cve-2020-1234"}, }, }, @@ -124,7 +124,7 @@ func TestEnrich(t *testing.T) { want: []scanner.Finding{ { CVEID: "DS002", Package: "Dockerfile", Severity: "MEDIUM", - RemediationText: "Upgrade or patch Dockerfile (currently ); no fixed version in DB", + RemediationText: "Upgrade or patch Dockerfile (currently ); no fixed version in DB", RemediationLinks: []string{"https://avd.aquasec.com/misconfig/ds002"}, }, }, diff --git a/pkg/report/report.go b/pkg/report/report.go index 767e683..015e78e 100644 --- a/pkg/report/report.go +++ b/pkg/report/report.go @@ -12,9 +12,9 @@ import ( // Options holds output options (formats, output dir, report base name). type Options struct { - Formats []string // "sarif", "markdown", "html", "csv" - OutputDir string - ReportBaseName string // Base name for report files (e.g. "report" or "report-20060102-150405"). Default "report". + Formats []string // "sarif", "markdown", "html", "csv" + OutputDir string + ReportBaseName string // Base name for report files (e.g. "report" or "report-20060102-150405"). Default "report". } // Generate writes SARIF, Markdown, HTML, and/or CSV from enriched findings to OutputDir. @@ -51,13 +51,13 @@ func Generate(findings []scanner.Finding, opts Options) error { // SARIF 2.1 minimal structure for Azure/GitHub Security tab. type sarifDoc struct { - Schema string `json:"$schema"` - Version string `json:"version"` + Schema string `json:"$schema"` + Version string `json:"version"` Runs []sarifRun `json:"runs"` } type sarifRun struct { - Tool sarifTool `json:"tool"` + Tool sarifTool `json:"tool"` Results []sarifResult `json:"results"` } @@ -66,17 +66,17 @@ type sarifTool struct { } type sarifDriver struct { - Name string `json:"name"` - Version string `json:"version"` - InformationURI string `json:"informationUri"` - Rules []sarifRule `json:"rules"` + Name string `json:"name"` + Version string `json:"version"` + InformationURI string `json:"informationUri"` + Rules []sarifRule `json:"rules"` } type sarifRule struct { - ID string `json:"id"` - Name string `json:"name"` - ShortDescription sarifMessage `json:"shortDescription"` - Help sarifHelp `json:"help,omitempty"` + ID string `json:"id"` + Name string `json:"name"` + ShortDescription sarifMessage `json:"shortDescription"` + Help sarifHelp `json:"help,omitempty"` } type sarifHelp struct { @@ -123,10 +123,10 @@ func writeSARIF(findings []scanner.Finding, path string) error { helpText += "\n\n" + strings.Join(f.RemediationLinks, "\n") } rules = append(rules, sarifRule{ - ID: ruleID, - Name: f.Title, + ID: ruleID, + Name: f.Title, ShortDescription: sarifMessage{Text: f.Title}, - Help: sarifHelp{Text: helpText}, + Help: sarifHelp{Text: helpText}, }) } level := severityToSarifLevel(f.Severity) diff --git a/pkg/scanner/finding.go b/pkg/scanner/finding.go index 1dfe6bf..4834e76 100644 --- a/pkg/scanner/finding.go +++ b/pkg/scanner/finding.go @@ -3,14 +3,14 @@ package scanner // Finding is the single finding model used by enricher and report generator. // Do not duplicate; enricher adds remediation fields. type Finding struct { - CVEID string `json:"cve_id"` - Package string `json:"package"` - CurrentVersion string `json:"current_version"` - FixedVersion string `json:"fixed_version,omitempty"` - Severity string `json:"severity"` - Title string `json:"title"` - Description string `json:"description,omitempty"` - RemediationText string `json:"remediation_text,omitempty"` + CVEID string `json:"cve_id"` + Package string `json:"package"` + CurrentVersion string `json:"current_version"` + FixedVersion string `json:"fixed_version,omitempty"` + Severity string `json:"severity"` + Title string `json:"title"` + Description string `json:"description,omitempty"` + RemediationText string `json:"remediation_text,omitempty"` RemediationLinks []string `json:"remediation_links,omitempty"` // Location: path where the vulnerability was found (e.g. PkgPath from Trivy, or target file/layer). FilePath string `json:"file_path,omitempty"` diff --git a/pkg/scanner/trivy.go b/pkg/scanner/trivy.go index 05cd6ee..72930e0 100644 --- a/pkg/scanner/trivy.go +++ b/pkg/scanner/trivy.go @@ -9,10 +9,10 @@ type trivyReport struct { } type trivyResult struct { - Target string `json:"Target"` - Class string `json:"Class"` - Type string `json:"Type"` - Vulnerabilities []trivyVuln `json:"Vulnerabilities"` + Target string `json:"Target"` + Class string `json:"Class"` + Type string `json:"Type"` + Vulnerabilities []trivyVuln `json:"Vulnerabilities"` } type trivyVuln struct { diff --git a/pkg/scanner/trivy_config.go b/pkg/scanner/trivy_config.go index f26a50b..9537d15 100644 --- a/pkg/scanner/trivy_config.go +++ b/pkg/scanner/trivy_config.go @@ -9,9 +9,9 @@ type trivyConfigReport struct { } type trivyConfigResult struct { - Target string `json:"Target"` - Class string `json:"Class"` - Type string `json:"Type"` + Target string `json:"Target"` + Class string `json:"Class"` + Type string `json:"Type"` Misconfigurations []trivyMisconfig `json:"Misconfigurations"` } diff --git a/pkg/scanner/trivy_test.go b/pkg/scanner/trivy_test.go index f227477..a7b5d3f 100644 --- a/pkg/scanner/trivy_test.go +++ b/pkg/scanner/trivy_test.go @@ -19,13 +19,13 @@ func Test_trivyVulnToFinding(t *testing.T) { } got := trivyVulnToFinding(v, "") want := Finding{ - CVEID: "CVE-2020-1234", - Package: "openssl", - CurrentVersion: "1.1.1a", - FixedVersion: "1.1.1b", - Severity: "HIGH", - Title: "OpenSSL issue", - Description: "Some description", + CVEID: "CVE-2020-1234", + Package: "openssl", + CurrentVersion: "1.1.1a", + FixedVersion: "1.1.1b", + Severity: "HIGH", + Title: "OpenSSL issue", + Description: "Some description", RemediationLinks: []string{"https://example.com/cve", "https://ref1.com", "https://ref2.com"}, } if got.CVEID != want.CVEID || got.Package != want.Package || got.CurrentVersion != want.CurrentVersion || @@ -54,9 +54,9 @@ func Test_trivyVulnToFinding_emptyPrimaryURL(t *testing.T) { func Test_trivyVulnToFinding_withPkgPathAndTarget(t *testing.T) { v := trivyVuln{ VulnerabilityID: "CVE-2020-1", - PkgName: "libfoo", - PkgPath: "/lib/libfoo.so.1", - Severity: "HIGH", + PkgName: "libfoo", + PkgPath: "/lib/libfoo.so.1", + Severity: "HIGH", } got := trivyVulnToFinding(v, "alpine 3.10") if got.FilePath != "/lib/libfoo.so.1" { @@ -82,14 +82,14 @@ func Test_trivyMisconfigToFinding(t *testing.T) { } got := trivyMisconfigToFinding(m, "Dockerfile") want := Finding{ - CVEID: "DS001", - Package: "Dockerfile", - CurrentVersion: "", - FixedVersion: "", - Severity: "HIGH", - Title: "Run as non-root", - Description: "Running as root is risky", - RemediationText: "Add USER directive", + CVEID: "DS001", + Package: "Dockerfile", + CurrentVersion: "", + FixedVersion: "", + Severity: "HIGH", + Title: "Run as non-root", + Description: "Running as root is risky", + RemediationText: "Add USER directive", RemediationLinks: []string{"https://avd.aquasec.com/misconfig/ds001", "https://ref.com"}, } if got.CVEID != want.CVEID || got.Package != want.Package || got.Severity != want.Severity || diff --git a/scanner.yaml b/scanner.yaml index bcf5bcc..e550a0c 100644 --- a/scanner.yaml +++ b/scanner.yaml @@ -11,3 +11,5 @@ output-name: report # Optional: fail the pipeline when findings match (CI) # fail-on-severity: CRITICAL,HIGH # fail-on-count: HIGH:5 + +# renovated: 2026-07-02 diff --git a/scanner.yaml.example b/scanner.yaml.example index bcf5bcc..e550a0c 100644 --- a/scanner.yaml.example +++ b/scanner.yaml.example @@ -11,3 +11,5 @@ output-name: report # Optional: fail the pipeline when findings match (CI) # fail-on-severity: CRITICAL,HIGH # fail-on-count: HIGH:5 + +# renovated: 2026-07-02 diff --git a/scripts/env-local.bat b/scripts/env-local.bat index d45487e..63deb05 100644 --- a/scripts/env-local.bat +++ b/scripts/env-local.bat @@ -9,3 +9,4 @@ echo PATH updated for this window. Trivy and Go are available. echo You can now run: run-tests.bat or run-scan-local.bat cmd /k REM To just set PATH and exit (e.g. for PowerShell): remove "cmd /k" above and use "call env-local.bat" before other commands. +REM renovated: 2026-07-02 diff --git a/scripts/install-deps.ps1 b/scripts/install-deps.ps1 index f36cdc8..3e78f09 100644 --- a/scripts/install-deps.ps1 +++ b/scripts/install-deps.ps1 @@ -140,3 +140,4 @@ Write-Host "`n=== Done. You can now build and run ===" -ForegroundColor Green Write-Host " go build -o scanner.exe ./cmd/cli" Write-Host " .\scanner.exe scan --image alpine:latest --output-dir ./reports" Write-Host "Optional: Install Docker to use the scanner as a container (docker build -t scanner:latest .)." +# renovated: 2026-07-02 diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh index ba67f74..56db3f3 100644 --- a/scripts/install-deps.sh +++ b/scripts/install-deps.sh @@ -161,3 +161,4 @@ go mod tidy echo "" echo "=== Done. You can now build and run: go build -o scanner ./cmd/cli && ./scanner scan --image alpine:latest --output-dir ./reports ===" echo "Optional: Install Docker to use the scanner as a container (docker build -t scanner:latest .)." +# renovated: 2026-07-02 diff --git a/scripts/prune-baseline-run.ps1 b/scripts/prune-baseline-run.ps1 index 7596173..c4f0b24 100644 --- a/scripts/prune-baseline-run.ps1 +++ b/scripts/prune-baseline-run.ps1 @@ -64,3 +64,4 @@ foreach ($img in $present) { docker rmi $img 2>&1 } Write-Host "Done." +# renovated: 2026-07-02 diff --git a/scripts/prune-images-last-hour.ps1 b/scripts/prune-images-last-hour.ps1 index c3b8a42..aa7f4de 100644 --- a/scripts/prune-images-last-hour.ps1 +++ b/scripts/prune-images-last-hour.ps1 @@ -38,3 +38,4 @@ foreach ($id in $toRemove) { docker rmi -f $id 2>&1 } Write-Host "Done." +# renovated: 2026-07-02 diff --git a/scripts/run-baseline.bat b/scripts/run-baseline.bat index ca8b2ed..d9205d9 100644 --- a/scripts/run-baseline.bat +++ b/scripts/run-baseline.bat @@ -15,3 +15,4 @@ REM Rate-limit friendly run (10 random images, 10s between pulls): set BASELINE_ go run ./cmd/baseline echo Results in test-results\ endlocal +REM renovated: 2026-07-02 diff --git a/scripts/run-scan-local.bat b/scripts/run-scan-local.bat index 1574310..c879268 100644 --- a/scripts/run-scan-local.bat +++ b/scripts/run-scan-local.bat @@ -18,3 +18,4 @@ echo Reports in %REPORTS% echo HTML report: %REPORTS%\report.html if /i "%~2"=="/publish" call "%~dp0serve-report.bat" endlocal +REM renovated: 2026-07-02 diff --git a/scripts/run-scan.bat b/scripts/run-scan.bat index b266c35..ed3949b 100644 --- a/scripts/run-scan.bat +++ b/scripts/run-scan.bat @@ -8,3 +8,4 @@ mkdir "%REPORTS%" 2>nul docker run --rm -v "%REPORTS%":/reports scanner:latest scan --image %IMAGE% --output-dir /reports --format sarif,markdown echo Reports in %REPORTS% endlocal +REM renovated: 2026-07-02 diff --git a/scripts/run-scan.sh b/scripts/run-scan.sh index 97669ce..d603183 100644 --- a/scripts/run-scan.sh +++ b/scripts/run-scan.sh @@ -5,3 +5,4 @@ REPORTS="$(cd "$(dirname "$0")/.." && pwd)/reports" mkdir -p "$REPORTS" docker run --rm -v "$REPORTS":/reports scanner:latest scan --image "$IMAGE" --output-dir /reports --format sarif,markdown echo "Reports in $REPORTS" +# renovated: 2026-07-02 diff --git a/scripts/run-tests.bat b/scripts/run-tests.bat index 2a0769c..efc74e1 100644 --- a/scripts/run-tests.bat +++ b/scripts/run-tests.bat @@ -22,3 +22,4 @@ if errorlevel 1 ( ) go test -tags=integration ./tests/integration/... -v -count=1 endlocal +REM renovated: 2026-07-02 diff --git a/scripts/run-workflow-test.ps1 b/scripts/run-workflow-test.ps1 index 1014096..bfd1ce7 100644 --- a/scripts/run-workflow-test.ps1 +++ b/scripts/run-workflow-test.ps1 @@ -83,3 +83,4 @@ foreach ($img in $images) { } Write-Host "`nDone. OK=$ok FAIL=$fail. Reports in $reportsDir (wf-*.md, wf-*.html)" +# renovated: 2026-07-02 diff --git a/scripts/run-workflow-test.sh b/scripts/run-workflow-test.sh index 4699687..feff146 100644 --- a/scripts/run-workflow-test.sh +++ b/scripts/run-workflow-test.sh @@ -78,3 +78,4 @@ done echo "" echo "Done. OK=$ok FAIL=$fail. Reports in $REPORTS_DIR (wf-*.md, wf-*.html)" +# renovated: 2026-07-02 diff --git a/scripts/serve-report.bat b/scripts/serve-report.bat index 4312758..8f436cd 100644 --- a/scripts/serve-report.bat +++ b/scripts/serve-report.bat @@ -30,3 +30,4 @@ start "" "%REPORTS%\report.html" echo Report opened. Location: %REPORTS%\report.html endlocal exit /b 0 +REM renovated: 2026-07-02 diff --git a/scripts/setup-and-test.ps1 b/scripts/setup-and-test.ps1 index 441a042..48757b0 100644 --- a/scripts/setup-and-test.ps1 +++ b/scripts/setup-and-test.ps1 @@ -146,3 +146,4 @@ if ($trivyOk) { } Write-Host "`n=== All done. ===" -ForegroundColor Green +# renovated: 2026-07-02 diff --git a/scripts/update-trivy-db.ps1 b/scripts/update-trivy-db.ps1 index 4ba8376..0735f4f 100644 --- a/scripts/update-trivy-db.ps1 +++ b/scripts/update-trivy-db.ps1 @@ -20,3 +20,4 @@ if (-not $trivy) { Write-Host "$(Get-Date -Format 'o') Updating Trivy DB..." & trivy image --download-db-only --cache-dir $CacheDir Write-Host "$(Get-Date -Format 'o') Trivy DB update done." +# renovated: 2026-07-02 diff --git a/scripts/update-trivy-db.sh b/scripts/update-trivy-db.sh index 4adb702..c5b5d8f 100644 --- a/scripts/update-trivy-db.sh +++ b/scripts/update-trivy-db.sh @@ -18,3 +18,4 @@ fi echo "$(date -Iseconds) Updating Trivy DB..." trivy image --download-db-only --cache-dir "$CACHE_DIR" echo "$(date -Iseconds) Trivy DB update done." +# renovated: 2026-07-02 diff --git a/tests/baseline/images-5-registries.txt b/tests/baseline/images-5-registries.txt index 317a20b..6f6bd11 100644 --- a/tests/baseline/images-5-registries.txt +++ b/tests/baseline/images-5-registries.txt @@ -16,3 +16,5 @@ quay.io/prometheus/prometheus:latest # Red Hat UBI registry.access.redhat.com/ubi8/ubi-minimal:latest + +# renovated: 2026-07-02 diff --git a/tests/baseline/images-hardened.txt b/tests/baseline/images-hardened.txt index bdaa9ca..05ee277 100644 --- a/tests/baseline/images-hardened.txt +++ b/tests/baseline/images-hardened.txt @@ -9,3 +9,5 @@ cgr.dev/chainguard/go:latest registry.access.redhat.com/ubi8/ubi-minimal:latest registry.access.redhat.com/ubi9/ubi-minimal:latest registry.access.redhat.com/ubi8/ubi-micro:latest + +# renovated: 2026-07-02 diff --git a/tests/baseline/images-lesser-known.txt b/tests/baseline/images-lesser-known.txt index 8298776..016d66e 100644 --- a/tests/baseline/images-lesser-known.txt +++ b/tests/baseline/images-lesser-known.txt @@ -53,3 +53,5 @@ eclipse-temurin:11-jre-alpine # Ubuntu older ubuntu:18.04 ubuntu:20.04 + +# renovated: 2026-07-02 diff --git a/tests/baseline/images-local.txt.example b/tests/baseline/images-local.txt.example index a0f44c8..7bf9cc6 100644 --- a/tests/baseline/images-local.txt.example +++ b/tests/baseline/images-local.txt.example @@ -7,3 +7,5 @@ # myregistry.company.com/myteam/api:v1 # harbor.myteam.io/project/worker:latest # localhost:5000/internal/base-node:hardened + +# renovated: 2026-07-02 diff --git a/tests/baseline/images-obscure.txt b/tests/baseline/images-obscure.txt index 035b73e..c5f6e1b 100644 --- a/tests/baseline/images-obscure.txt +++ b/tests/baseline/images-obscure.txt @@ -63,3 +63,5 @@ public.ecr.aws/docker/library/debian:bullseye-slim # --- Red Hat UBI (older or alternate) — registry.access.redhat.com --- registry.access.redhat.com/ubi8/ubi-minimal:8.8 registry.access.redhat.com/ubi9/ubi-minimal:latest + +# renovated: 2026-07-02 diff --git a/tests/baseline/images-other-registries.txt b/tests/baseline/images-other-registries.txt index 2ed0e0c..2e051a2 100644 --- a/tests/baseline/images-other-registries.txt +++ b/tests/baseline/images-other-registries.txt @@ -47,3 +47,5 @@ registry.access.redhat.com/ubi8/ubi-micro:latest # gcr.io/google-samples/hello-app:1.0 # gcr.io/distroless/static:nonroot # gcr.io/google-containers/pause:3.9 + +# renovated: 2026-07-02 diff --git a/tests/baseline/images-workflow-test.txt b/tests/baseline/images-workflow-test.txt index f0cfee8..dabe19e 100644 --- a/tests/baseline/images-workflow-test.txt +++ b/tests/baseline/images-workflow-test.txt @@ -20,3 +20,5 @@ registry.access.redhat.com/ubi8/ubi-minimal:latest # --- Chainguard (cgr.dev) --- cgr.dev/chainguard/wolfi-base:latest + +# renovated: 2026-07-02 diff --git a/tests/baseline/images.txt b/tests/baseline/images.txt index 152513e..222a30a 100644 --- a/tests/baseline/images.txt +++ b/tests/baseline/images.txt @@ -152,3 +152,5 @@ cgr.dev/chainguard/go:latest registry.access.redhat.com/ubi8/ubi-minimal:latest registry.access.redhat.com/ubi9/ubi-minimal:latest registry.access.redhat.com/ubi8/ubi-micro:latest + +# renovated: 2026-07-02 diff --git a/tests/integration/scan_integration_test.go b/tests/integration/scan_integration_test.go index 5c4526b..c5006f4 100644 --- a/tests/integration/scan_integration_test.go +++ b/tests/integration/scan_integration_test.go @@ -63,3 +63,5 @@ func TestScanRealImage(t *testing.T) { t.Logf("RUN_INTEGRATION_STRICT=1: expected at least one finding for %s (DB may be empty or image updated)", integrationImage) } } + +// renovated: 2026-07-02 diff --git a/tests/integration/scan_with_config_test.go b/tests/integration/scan_with_config_test.go index 8d42792..f5f38b7 100644 --- a/tests/integration/scan_with_config_test.go +++ b/tests/integration/scan_with_config_test.go @@ -112,3 +112,5 @@ func splitTrim(s, sep string) []string { } return out } + +// renovated: 2026-07-02 diff --git a/web/index.html b/web/index.html index d1ba4e1..cb4a9ed 100644 --- a/web/index.html +++ b/web/index.html @@ -809,3 +809,5 @@

Scan a container image instantly

+ +