Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cursor/rules/docs-ux.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .cursor/rules/documentation-agent.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .cursor/rules/enterprise-ops.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .cursor/rules/integrations.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .cursor/rules/lessons-learned-agent.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .cursor/rules/scanner-core.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .cursor/rules/web-frontend.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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).

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ test-results/
# OS
.DS_Store
Thumbs.db

# renovated: 2026-07-02
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
# ─────────────────────────────────────────────────────────────────────────────
ENTRYPOINT ["scanner"]
CMD ["scan", "--help"]

# renovated: 2026-07-02
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
28 changes: 14 additions & 14 deletions cmd/baseline/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -361,28 +361,28 @@ 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 {
// Build per-image rows (nomenclature: image name/path)
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
Expand Down
6 changes: 3 additions & 3 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <ref> [options] (Docker/Podman image)")
Expand Down
18 changes: 9 additions & 9 deletions cmd/mcp-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,5 @@ services:
volumes:
trivy-cache:
driver: local

# renovated: 2026-07-02
2 changes: 2 additions & 0 deletions docs/DOCUMENTATION-AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- renovated: 2026-07-02 -->
42 changes: 21 additions & 21 deletions docs/HELP.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ You can scan **container images** (Docker, Podman, containerd) with `--image <re

**Docker Container Scanner** is a tool that **checks a container image for known security problems** (vulnerabilities). It gives you a **report** that lists what it found and **how to fix each one** (for example: “Upgrade this package from version X to version Y”).

You can run it from the command line (CLI) or open the web page to get the exact command. It fits into your build pipeline (e.g. Azure, GitHub, GitLab, Jenkins) so every build can be checked automatically.
You can run it from the command line (CLI) or open the web page to get the exact command. It fits into your build pipeline (e.g. GitHub Actions, GitLab CI, Azure DevOps, Jenkins, CircleCI, AWS CodeBuild, Google Cloud Build, Bitbucket Pipelines, Tekton) so every build can be checked automatically.

---

Expand All @@ -42,10 +42,10 @@ It depends how you want to run the scanner:

**One script to install dependencies (Go + Trivy) on your machine:**

- **Linux or macOS:** From the project folder, run:
`./scripts/install-deps.sh`
- **Linux or macOS:** From the project folder, run:
`./scripts/install-deps.sh`
(If you get “permission denied”, run: `chmod +x scripts/install-deps.sh` first.)
- **Windows (PowerShell):** From the project folder, run:
- **Windows (PowerShell):** From the project folder, run:
`.\scripts\install-deps.ps1`

The script runs **in the background by default** so you can keep using your terminal. It writes a log file (`install-deps.log` in the project folder). To run it in the foreground and wait for it to finish, use:
Expand All @@ -61,15 +61,15 @@ After it finishes, you can build and run the scanner from source. If you prefer

If Go or Trivy were installed by the script to a folder under the project (e.g. `.go/go/bin` or `.trivy/...`), they might not be in your **PATH** in new terminals. PATH is the list of folders your system searches when you type a command.

- **Windows:**
- **Temporary (current session):** In PowerShell run
`$env:Path = "C:\path\to\docker-scanner\.go\go\bin;C:\path\to\docker-scanner\.trivy\...;$env:Path"`
(replace with the actual paths the install script printed.)
- **Windows:**
- **Temporary (current session):** In PowerShell run
`$env:Path = "C:\path\to\docker-scanner\.go\go\bin;C:\path\to\docker-scanner\.trivy\...;$env:Path"`
(replace with the actual paths the install script printed.)
- **Permanent:** Open **System** → **Advanced system settings** → **Environment Variables**. Under “User variables” or “System variables”, select **Path** → **Edit** → **New**, and add the folder that contains `go.exe` and the folder that contains `trivy.exe`. OK out. New terminals will then find `go` and `trivy`.

- **Linux / macOS:**
Add to your shell config file (e.g. `~/.bashrc` or `~/.zshrc`):
`export PATH="/path/to/docker-scanner/.go/go/bin:/path/to/docker-scanner/.trivy/...:$PATH"`
- **Linux / macOS:**
Add to your shell config file (e.g. `~/.bashrc` or `~/.zshrc`):
`export PATH="/path/to/docker-scanner/.go/go/bin:/path/to/docker-scanner/.trivy/...:$PATH"`
(use the paths the install script printed.) Then run `source ~/.bashrc` (or open a new terminal).

---
Expand All @@ -78,10 +78,10 @@ If Go or Trivy were installed by the script to a folder under the project (e.g.

Trivy uses a **vulnerability database** that is updated regularly. For fresher results, update it about **once a day**:

- **Linux/macOS:** Run `./scripts/update-trivy-db.sh` (from the project root). To run it automatically every day, add a **cron** job:
`0 3 * * * /full/path/to/docker-scanner/scripts/update-trivy-db.sh`
- **Linux/macOS:** Run `./scripts/update-trivy-db.sh` (from the project root). To run it automatically every day, add a **cron** job:
`0 3 * * * /full/path/to/docker-scanner/scripts/update-trivy-db.sh`
(e.g. run `crontab -e` and add that line; 3:00 AM daily.)
- **Windows:** Run `.\scripts\update-trivy-db.ps1`. To run it automatically every day, use **Task Scheduler**: create a daily task that runs
- **Windows:** Run `.\scripts\update-trivy-db.ps1`. To run it automatically every day, use **Task Scheduler**: create a daily task that runs
`powershell -File "C:\path\to\docker-scanner\scripts\update-trivy-db.ps1"`.

---
Expand Down Expand Up @@ -168,14 +168,14 @@ Then look at the **Critical** count. Even if none are marked exploitable yet, Cr

## What do I do first?

1. **Choose how to run the scanner**
- **Easiest:** Use Docker. Install Docker, then build the scanner image once and run it (see [Getting started](getting-started.md)).
1. **Choose how to run the scanner**
- **Easiest:** Use Docker. Install Docker, then build the scanner image once and run it (see [Getting started](getting-started.md)).
- **From source:** Run the [install-deps script](#what-do-i-need-installed) for your OS, then build and run the scanner.

2. **Run your first scan**
2. **Run your first scan**
You give the scanner an image name (e.g. `alpine:latest` or `myapp:v1`). It looks up known vulnerabilities for the software inside that image and writes a report.

3. **Open the report**
3. **Open the report**
You get files like `report.md` (readable), `report.html`, and `report.sarif` (for Azure/GitHub Security). Open the Markdown or HTML file to see what was found and how to fix it.

---
Expand All @@ -190,11 +190,11 @@ A **baseline** run means scanning **many images at once** (e.g. 100+ or a list y

If you ran a baseline with “pull before scan,” your machine will have downloaded many images. To free disk space:

- **Remove images from a specific run:**
- **Remove images from a specific run:**
`.\scripts\prune-baseline-run.ps1` (Windows) or use the latest CSV path. See [Baseline — After the run](baseline.md#after-the-run-results-and-cleanup).
- **Remove images pulled in the last N hours:**
- **Remove images pulled in the last N hours:**
`.\scripts\prune-images-last-hour.ps1 -Hours 6` (Windows). On Linux/macOS you can run the same logic or use `docker image prune -a`.
- **Remove all unused images:**
- **Remove all unused images:**
`docker image prune -a` (review the list before confirming).

---
Expand Down
2 changes: 2 additions & 0 deletions docs/baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,5 @@ Run scripts from repo root. We recommend doing this after you've saved or publis

- **CSV** – `--format csv` (or add to `--format sarif,markdown,html,csv`) for spreadsheets.
- **PDF** – The scanner does not generate PDF directly. Use “Print to PDF” in the browser on `report.html`, or convert the Markdown report with an external tool.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,5 @@ scanner db update [--cache-dir <dir>]

- Registry auth: use Docker login (`docker login <registry>`) 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.

<!-- renovated: 2026-07-02 -->
3 changes: 2 additions & 1 deletion docs/docker-scanner.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
"path": ".."
}
]
}
}

2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**
Expand Down
2 changes: 2 additions & 0 deletions docs/hardened-images-and-local-registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <registry>`, 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). |

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions docs/ide-and-mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions docs/image-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <registry>` (or CI secrets) where you have access.

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions docs/system-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

<!-- renovated: 2026-07-02 -->
2 changes: 2 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,5 @@ trivy image --format json <your-image>

- 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.

<!-- renovated: 2026-07-02 -->
Loading
Loading