@osfactory/har 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/templates/har-boilerplate/STAGES.md +4 -2
- package/dist/templates/har-boilerplate-cli/STAGES.md +4 -2
- package/dist/templates/har-boilerplate-ios/STAGES.md +4 -2
- package/dist/templates/plugins/gitleaks/.github/workflows/gitleaks.yml +29 -0
- package/dist/templates/plugins/gitleaks/.gitleaks.toml +17 -0
- package/dist/templates/plugins/gitleaks/.har/stages/GITLEAKS.md +77 -0
- package/dist/templates/plugins/gitleaks/.har/stages/secrets-scan.sh +137 -0
- package/dist/templates/plugins/gitleaks/README.md +34 -0
- package/dist/templates/plugins/gitleaks/template.manifest.json +37 -0
- package/dist/templates/plugins/trivy/.github/workflows/trivy.yml +40 -0
- package/dist/templates/plugins/trivy/.har/stages/TRIVY.md +88 -0
- package/dist/templates/plugins/trivy/.har/stages/vuln-scan.sh +157 -0
- package/dist/templates/plugins/trivy/.trivyignore +9 -0
- package/dist/templates/plugins/trivy/README.md +32 -0
- package/dist/templates/plugins/trivy/template.manifest.json +44 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -69193,7 +69193,7 @@ function harnessExists(repoPath) {
|
|
|
69193
69193
|
// src/harness/plugins.ts
|
|
69194
69194
|
init_schema2();
|
|
69195
69195
|
init_stages();
|
|
69196
|
-
var PLUGIN_IDS = ["playwright", "rocketsim", "kerno"];
|
|
69196
|
+
var PLUGIN_IDS = ["playwright", "rocketsim", "kerno", "gitleaks", "trivy"];
|
|
69197
69197
|
var PluginManifestFileSchema = external_exports.object({
|
|
69198
69198
|
src: external_exports.string().min(1),
|
|
69199
69199
|
dest: external_exports.string().min(1),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HAR Stages — authoring guide
|
|
2
2
|
|
|
3
3
|
Stages are the harness's single vocabulary for runnable checks and lifecycle
|
|
4
|
-
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`), your
|
|
4
|
+
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`, `gitleaks`, `trivy`), your
|
|
5
5
|
project's test/lint commands, bespoke validation scripts — registers in
|
|
6
6
|
`.har/stages.json` with the same schema, and agents interact with stages only
|
|
7
7
|
through that registry (CLI `har env verify`, MCP `har_run_stage` /
|
|
@@ -91,6 +91,8 @@ committed.
|
|
|
91
91
|
|
|
92
92
|
`har env add-plugin --list` shows available plugins; `har env add-plugin
|
|
93
93
|
playwright` (web), `har env add-plugin rocketsim` (iOS), or `har env
|
|
94
|
-
add-plugin kerno` (backend)
|
|
94
|
+
add-plugin kerno` (backend), `har env add-plugin gitleaks` (secrets scanning, any
|
|
95
|
+
stack), or `har env add-plugin trivy` (dependency + IaC security scan, any stack)
|
|
96
|
+
installs one. A
|
|
95
97
|
plugin is just packaging: it copies files, merges `package.json` fragments,
|
|
96
98
|
and registers stages through the exact same registry as `add-stage --custom`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HAR Stages — authoring guide
|
|
2
2
|
|
|
3
3
|
Stages are the harness's single vocabulary for runnable checks and lifecycle
|
|
4
|
-
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`), your
|
|
4
|
+
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`, `gitleaks`, `trivy`), your
|
|
5
5
|
project's test/lint commands, bespoke validation scripts — registers in
|
|
6
6
|
`.har/stages.json` with the same schema, and agents interact with stages only
|
|
7
7
|
through that registry (CLI `har env verify`, MCP `har_run_stage` /
|
|
@@ -91,6 +91,8 @@ committed.
|
|
|
91
91
|
|
|
92
92
|
`har env add-plugin --list` shows available plugins; `har env add-plugin
|
|
93
93
|
playwright` (web), `har env add-plugin rocketsim` (iOS), or `har env
|
|
94
|
-
add-plugin kerno` (backend)
|
|
94
|
+
add-plugin kerno` (backend), `har env add-plugin gitleaks` (secrets scanning, any
|
|
95
|
+
stack), or `har env add-plugin trivy` (dependency + IaC security scan, any stack)
|
|
96
|
+
installs one. A
|
|
95
97
|
plugin is just packaging: it copies files, merges `package.json` fragments,
|
|
96
98
|
and registers stages through the exact same registry as `add-stage --custom`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HAR Stages — authoring guide
|
|
2
2
|
|
|
3
3
|
Stages are the harness's single vocabulary for runnable checks and lifecycle
|
|
4
|
-
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`), your
|
|
4
|
+
actions. Everything — shipped plugins (`playwright`, `rocketsim`, `kerno`, `gitleaks`, `trivy`), your
|
|
5
5
|
project's test/lint commands, bespoke validation scripts — registers in
|
|
6
6
|
`.har/stages.json` with the same schema, and agents interact with stages only
|
|
7
7
|
through that registry (CLI `har env verify`, MCP `har_run_stage` /
|
|
@@ -91,6 +91,8 @@ committed.
|
|
|
91
91
|
|
|
92
92
|
`har env add-plugin --list` shows available plugins; `har env add-plugin
|
|
93
93
|
playwright` (web), `har env add-plugin rocketsim` (iOS), or `har env
|
|
94
|
-
add-plugin kerno` (backend)
|
|
94
|
+
add-plugin kerno` (backend), `har env add-plugin gitleaks` (secrets scanning, any
|
|
95
|
+
stack), or `har env add-plugin trivy` (dependency + IaC security scan, any stack)
|
|
96
|
+
installs one. A
|
|
95
97
|
plugin is just packaging: it copies files, merges `package.json` fragments,
|
|
96
98
|
and registers stages through the exact same registry as `add-stage --custom`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Secrets scanning with the official Gitleaks action.
|
|
2
|
+
# Installed by `har env add-plugin gitleaks` (skip with --skip-ci).
|
|
3
|
+
#
|
|
4
|
+
# This CI run is what produces org-level scanning evidence that compliance
|
|
5
|
+
# platforms (Vanta, Drata, …) can ingest via your GitHub organization.
|
|
6
|
+
# Local .har stage runs are pre-merge protection only.
|
|
7
|
+
#
|
|
8
|
+
# GITLEAKS_LICENSE is required for organization repos (free — see
|
|
9
|
+
# https://github.com/gitleaks/gitleaks-action#environment-variables);
|
|
10
|
+
# personal accounts can omit it.
|
|
11
|
+
name: gitleaks
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
pull_request:
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
scan:
|
|
20
|
+
name: gitleaks
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
27
|
+
env:
|
|
28
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Gitleaks configuration — installed by `har env add-plugin gitleaks`.
|
|
2
|
+
# Extends the default ruleset; tune allowlists per repo.
|
|
3
|
+
# Docs: https://github.com/gitleaks/gitleaks#configuration
|
|
4
|
+
|
|
5
|
+
[extend]
|
|
6
|
+
useDefault = true
|
|
7
|
+
|
|
8
|
+
[allowlist]
|
|
9
|
+
description = "HAR harness defaults — dependencies, build outputs, harness artifacts"
|
|
10
|
+
paths = [
|
|
11
|
+
'''node_modules/''',
|
|
12
|
+
'''dist/''',
|
|
13
|
+
'''build/''',
|
|
14
|
+
'''vendor/''',
|
|
15
|
+
'''\.git/''',
|
|
16
|
+
'''\.har/artifacts/''',
|
|
17
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Gitleaks secrets scan — authoring & adaptation guide
|
|
2
|
+
|
|
3
|
+
The `secrets-scan` stage runs [Gitleaks](https://github.com/gitleaks/gitleaks)
|
|
4
|
+
against the agent's work dir and fails when hardcoded secrets are found. It is a
|
|
5
|
+
**pre-merge, shift-left** check: it catches credentials before they ever reach the
|
|
6
|
+
default branch.
|
|
7
|
+
|
|
8
|
+
## Prerequisite
|
|
9
|
+
|
|
10
|
+
The `gitleaks` binary must be on PATH:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
brew install gitleaks # macOS / Linuxbrew
|
|
14
|
+
# or download a release binary:
|
|
15
|
+
# https://github.com/gitleaks/gitleaks/releases
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The stage fails fast with an install hint when the binary is missing.
|
|
19
|
+
|
|
20
|
+
## Running
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
./.har/stages/secrets-scan.sh <agent-id> # dir mode (default)
|
|
24
|
+
./.har/stages/secrets-scan.sh <agent-id> git # history mode
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| Mode | What it scans | Use when |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `dir` (default) | Every file currently in the work dir, **including uncommitted changes** | Normal verification — agents usually have uncommitted edits |
|
|
30
|
+
| `git` | The full git history of the worktree | Auditing that a secret never entered history (e.g. committed then removed) |
|
|
31
|
+
|
|
32
|
+
`git` mode on a repository with pre-existing leaks in old commits will fail on
|
|
33
|
+
findings unrelated to the current session. Generate a
|
|
34
|
+
[baseline](https://github.com/gitleaks/gitleaks#baseline) once and ignore known
|
|
35
|
+
history, or stick to `dir` mode for per-session verification.
|
|
36
|
+
|
|
37
|
+
## Artifacts
|
|
38
|
+
|
|
39
|
+
Written to the **main repo root**, not the worktree:
|
|
40
|
+
|
|
41
|
+
| Path | Content |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `.har/artifacts/secrets-scan/report.json` | JSON findings (rule, file, line — secret values are **redacted** via `--redact`) |
|
|
44
|
+
| `.har/artifacts/secrets-scan/gitleaks.log` | Full scanner output for debugging |
|
|
45
|
+
|
|
46
|
+
The stage prints a JSON summary to stdout: `status`, `leaks_found`, and the first
|
|
47
|
+
20 findings. Exit code 0 = clean, 1 = leaks found, anything else = scanner error.
|
|
48
|
+
|
|
49
|
+
## Tuning what gets flagged
|
|
50
|
+
|
|
51
|
+
The plugin ships a root `.gitleaks.toml` that extends the default ruleset and
|
|
52
|
+
allowlists dependency/build directories (`node_modules`, `dist`, `build`,
|
|
53
|
+
`vendor`, `.git`, `.har/artifacts`). Adapt it per repo:
|
|
54
|
+
|
|
55
|
+
- **False positive in a specific line** — append `# gitleaks:allow` to that line.
|
|
56
|
+
- **Test fixtures with dummy keys** — add the path to `[allowlist] paths` in
|
|
57
|
+
`.gitleaks.toml` (entries are regexes).
|
|
58
|
+
- **Custom internal token formats** — add `[[rules]]` entries; the shipped config
|
|
59
|
+
keeps `useDefault = true`, so default rules stay active.
|
|
60
|
+
|
|
61
|
+
Gitleaks automatically picks up `.gitleaks.toml` at the scan target root, so the
|
|
62
|
+
same config governs local stage runs and CI.
|
|
63
|
+
|
|
64
|
+
## CI and compliance platforms (Vanta, Drata, …)
|
|
65
|
+
|
|
66
|
+
The optional workflow `.github/workflows/gitleaks.yml` (skipped with `--skip-ci`)
|
|
67
|
+
runs the official [`gitleaks/gitleaks-action`](https://github.com/gitleaks/gitleaks-action)
|
|
68
|
+
on pushes and pull requests.
|
|
69
|
+
|
|
70
|
+
Note the split: **this stage's local runs produce no compliance evidence** — they
|
|
71
|
+
exist so secrets never land on the default branch. Compliance platforms such as
|
|
72
|
+
Vanta ingest evidence from org-level sources (your GitHub organization, code
|
|
73
|
+
scanning results, etc.), which the CI workflow feeds. Keep both: the local stage
|
|
74
|
+
prevents findings; the CI run proves scanning happens.
|
|
75
|
+
|
|
76
|
+
Organizations (not personal accounts) need a free `GITLEAKS_LICENSE` secret for
|
|
77
|
+
the action — see the workflow comments.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Gitleaks secrets scan for an agent slot.
|
|
3
|
+
# Scans the agent work dir for hardcoded secrets and writes a redacted JSON report.
|
|
4
|
+
# Outputs JSON to stdout, human-readable progress to stderr.
|
|
5
|
+
#
|
|
6
|
+
# Usage: ./.har/stages/secrets-scan.sh <agent-id> [mode]
|
|
7
|
+
# mode (optional): dir (default) — scan the working tree, incl. uncommitted files
|
|
8
|
+
# git — scan the full git history of the worktree
|
|
9
|
+
#
|
|
10
|
+
# Prerequisite: gitleaks on PATH (brew install gitleaks, or a release binary
|
|
11
|
+
# from https://github.com/gitleaks/gitleaks/releases)
|
|
12
|
+
# See: ./.har/stages/GITLEAKS.md for the adaptation guide.
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
HARNESS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
17
|
+
REPO_ROOT="$(cd "$HARNESS_DIR/.." && pwd)"
|
|
18
|
+
# agent-slot.sh expects SCRIPT_DIR to be .har/ (slot registry lives there)
|
|
19
|
+
SCRIPT_DIR="$HARNESS_DIR"
|
|
20
|
+
|
|
21
|
+
# shellcheck source=/dev/null
|
|
22
|
+
source "$HARNESS_DIR/harness.env"
|
|
23
|
+
# shellcheck source=/dev/null
|
|
24
|
+
source "$HARNESS_DIR/agent-slot.sh"
|
|
25
|
+
|
|
26
|
+
AGENT_ID="${1:?Usage: secrets-scan.sh <agent-id> [dir|git]}"
|
|
27
|
+
MODE="${2:-dir}"
|
|
28
|
+
|
|
29
|
+
validate_agent_id "$AGENT_ID"
|
|
30
|
+
|
|
31
|
+
if [ "$MODE" != "dir" ] && [ "$MODE" != "git" ]; then
|
|
32
|
+
echo "Error: unknown mode '$MODE' — expected 'dir' or 'git'." >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
log() { echo "==> [secrets-scan agent-$AGENT_ID] $*" >&2; }
|
|
37
|
+
|
|
38
|
+
# ── Preflight ─────────────────────────────────────────────────────────────────
|
|
39
|
+
if ! command -v gitleaks >/dev/null 2>&1; then
|
|
40
|
+
echo "Error: gitleaks not found on PATH." >&2
|
|
41
|
+
echo " Install: brew install gitleaks" >&2
|
|
42
|
+
echo " Or download a release binary: https://github.com/gitleaks/gitleaks/releases" >&2
|
|
43
|
+
echo " Then re-run: ./.har/stages/secrets-scan.sh ${AGENT_ID}" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ── Artifact directory ────────────────────────────────────────────────────────
|
|
48
|
+
# Resolved BEFORE sourcing .env.agent.<id>, which overrides REPO_ROOT with the
|
|
49
|
+
# worktree path — artifacts must land in the main repo root per convention.
|
|
50
|
+
ARTIFACT_DIR="$REPO_ROOT/.har/artifacts/secrets-scan"
|
|
51
|
+
mkdir -p "$ARTIFACT_DIR"
|
|
52
|
+
REPORT_PATH="$ARTIFACT_DIR/report.json"
|
|
53
|
+
SCAN_LOG="$ARTIFACT_DIR/gitleaks.log"
|
|
54
|
+
|
|
55
|
+
# ── Resolve agent env ─────────────────────────────────────────────────────────
|
|
56
|
+
ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
|
|
57
|
+
echo "No .env.agent.${AGENT_ID} found." >&2
|
|
58
|
+
har_suggest_launch "$AGENT_ID" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
set -a
|
|
63
|
+
# shellcheck source=/dev/null
|
|
64
|
+
source "$ENV_FILE"
|
|
65
|
+
set +a
|
|
66
|
+
|
|
67
|
+
WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
|
|
68
|
+
|
|
69
|
+
GITLEAKS_VERSION="$(gitleaks version 2>/dev/null | head -1 || echo unknown)"
|
|
70
|
+
log "gitleaks ${GITLEAKS_VERSION} — ${MODE} scan of $WORK_DIR"
|
|
71
|
+
|
|
72
|
+
# ── Scan ──────────────────────────────────────────────────────────────────────
|
|
73
|
+
# --exit-code 99 distinguishes "leaks found" (99) from tool errors (other non-zero).
|
|
74
|
+
# --redact keeps discovered secret values out of the report artifact.
|
|
75
|
+
START=$(now_ms)
|
|
76
|
+
|
|
77
|
+
set +e
|
|
78
|
+
gitleaks "$MODE" "$WORK_DIR" \
|
|
79
|
+
--report-format json \
|
|
80
|
+
--report-path "$REPORT_PATH" \
|
|
81
|
+
--exit-code 99 \
|
|
82
|
+
--redact \
|
|
83
|
+
--no-banner \
|
|
84
|
+
>"$SCAN_LOG" 2>&1
|
|
85
|
+
SCAN_EXIT=$?
|
|
86
|
+
set -e
|
|
87
|
+
|
|
88
|
+
END=$(now_ms)
|
|
89
|
+
TOTAL_MS=$(( END - START ))
|
|
90
|
+
|
|
91
|
+
if [ "$SCAN_EXIT" != "0" ] && [ "$SCAN_EXIT" != "99" ]; then
|
|
92
|
+
log "gitleaks failed (exit $SCAN_EXIT) — last log lines:"
|
|
93
|
+
tail -20 "$SCAN_LOG" | sed 's/^/ /' >&2
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if [ "$SCAN_EXIT" = "99" ]; then
|
|
97
|
+
log "Leaks detected — see $REPORT_PATH (redacted)"
|
|
98
|
+
else
|
|
99
|
+
log "Scan finished in ${TOTAL_MS}ms (exit $SCAN_EXIT)"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# ── Output ────────────────────────────────────────────────────────────────────
|
|
103
|
+
REPORT_PATH="$REPORT_PATH" SCAN_EXIT="$SCAN_EXIT" TOTAL_MS="$TOTAL_MS" \
|
|
104
|
+
AGENT_ID="$AGENT_ID" MODE="$MODE" node -e "
|
|
105
|
+
const fs = require('fs');
|
|
106
|
+
const scanExit = Number(process.env.SCAN_EXIT);
|
|
107
|
+
let leaks = [];
|
|
108
|
+
try {
|
|
109
|
+
leaks = JSON.parse(fs.readFileSync(process.env.REPORT_PATH, 'utf8')) || [];
|
|
110
|
+
} catch { /* no report written (older gitleaks writes none on a clean scan) */ }
|
|
111
|
+
const status = scanExit === 0 ? 'pass' : 'fail';
|
|
112
|
+
const out = {
|
|
113
|
+
status,
|
|
114
|
+
stageId: 'secrets-scan',
|
|
115
|
+
kind: 'test',
|
|
116
|
+
agent_id: Number(process.env.AGENT_ID),
|
|
117
|
+
total_ms: Number(process.env.TOTAL_MS),
|
|
118
|
+
mode: process.env.MODE,
|
|
119
|
+
leaks_found: leaks.length,
|
|
120
|
+
findings: leaks.slice(0, 20).map((l) => ({
|
|
121
|
+
rule: l.RuleID,
|
|
122
|
+
file: l.File,
|
|
123
|
+
line: l.StartLine,
|
|
124
|
+
commit: l.Commit || undefined,
|
|
125
|
+
})),
|
|
126
|
+
error: scanExit !== 0 && scanExit !== 99 ? 'gitleaks exited ' + scanExit + ' — see .har/artifacts/secrets-scan/gitleaks.log' : undefined,
|
|
127
|
+
artifacts: [
|
|
128
|
+
{ path: '.har/artifacts/secrets-scan', kind: 'directory' }
|
|
129
|
+
]
|
|
130
|
+
};
|
|
131
|
+
process.stdout.write(JSON.stringify(out, null, 2) + '\n');
|
|
132
|
+
"
|
|
133
|
+
|
|
134
|
+
if [ "$SCAN_EXIT" = "99" ]; then
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
exit "$SCAN_EXIT"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Gitleaks plugin
|
|
2
|
+
|
|
3
|
+
Installs a `secrets-scan` stage that runs [Gitleaks](https://github.com/gitleaks/gitleaks)
|
|
4
|
+
against the agent work dir, so hardcoded credentials fail verification before
|
|
5
|
+
they reach the default branch.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
har env add-plugin gitleaks
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
What it adds:
|
|
12
|
+
|
|
13
|
+
- `.har/stages/secrets-scan.sh` — the stage script (registered in `.har/stages.json`
|
|
14
|
+
and appended to `verificationStages`)
|
|
15
|
+
- `.har/stages/GITLEAKS.md` — adaptation guide
|
|
16
|
+
- `.gitleaks.toml` — default config extending the built-in ruleset with harness
|
|
17
|
+
allowlists (skipped if the repo already has one)
|
|
18
|
+
- `.github/workflows/gitleaks.yml` — official `gitleaks/gitleaks-action` CI
|
|
19
|
+
workflow (skip with `--skip-ci`)
|
|
20
|
+
|
|
21
|
+
Requirements: the `gitleaks` binary on PATH (`brew install gitleaks` or a
|
|
22
|
+
[release binary](https://github.com/gitleaks/gitleaks/releases)). No package.json
|
|
23
|
+
changes — Gitleaks is an external tool, like RocketSim.
|
|
24
|
+
|
|
25
|
+
Run it:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
./.har/stages/secrets-scan.sh 1 # scan the working tree (default)
|
|
29
|
+
./.har/stages/secrets-scan.sh 1 git # scan full git history
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Findings land in `.har/artifacts/secrets-scan/report.json` (secret values
|
|
33
|
+
redacted). See `.har/stages/GITLEAKS.md` for tuning allowlists, baselines for
|
|
34
|
+
noisy history, and how the CI workflow feeds compliance platforms like Vanta.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "gitleaks",
|
|
3
|
+
"stageId": "secrets-scan",
|
|
4
|
+
"verificationStages": [
|
|
5
|
+
"secrets-scan"
|
|
6
|
+
],
|
|
7
|
+
"stage": {
|
|
8
|
+
"id": "secrets-scan",
|
|
9
|
+
"kind": "test",
|
|
10
|
+
"description": "Gitleaks secrets scan (working tree by default; pass 'git' to scan history)",
|
|
11
|
+
"script": "stages/secrets-scan.sh",
|
|
12
|
+
"requiresAgentId": true,
|
|
13
|
+
"artifacts": [
|
|
14
|
+
{
|
|
15
|
+
"path": ".har/artifacts/secrets-scan",
|
|
16
|
+
"kind": "directory",
|
|
17
|
+
"description": "Redacted JSON findings report and scan log"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
{ "src": ".har/stages/secrets-scan.sh", "dest": ".har/stages/secrets-scan.sh", "executable": true },
|
|
23
|
+
{ "src": ".har/stages/GITLEAKS.md", "dest": ".har/stages/GITLEAKS.md" }
|
|
24
|
+
],
|
|
25
|
+
"optionalFiles": [
|
|
26
|
+
{ "src": ".gitleaks.toml", "dest": ".gitleaks.toml" },
|
|
27
|
+
{ "src": ".github/workflows/gitleaks.yml", "dest": ".github/workflows/gitleaks.yml", "skipFlag": "skipCi" }
|
|
28
|
+
],
|
|
29
|
+
"nextSteps": [
|
|
30
|
+
"Install gitleaks: brew install gitleaks — or a release binary from https://github.com/gitleaks/gitleaks/releases",
|
|
31
|
+
"./.har/launch.sh 1",
|
|
32
|
+
"./.har/stages/secrets-scan.sh 1",
|
|
33
|
+
"Open .har/artifacts/secrets-scan/report.json for findings (secrets are redacted)",
|
|
34
|
+
"Tune allowlists in .gitleaks.toml — read .har/stages/GITLEAKS.md for the adaptation guide"
|
|
35
|
+
],
|
|
36
|
+
"docsPath": ".har/stages/GITLEAKS.md"
|
|
37
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Trivy scan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
scan:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
security-events: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- name: Run Trivy (filesystem scan)
|
|
17
|
+
uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0
|
|
18
|
+
with:
|
|
19
|
+
scan-type: fs
|
|
20
|
+
scan-ref: .
|
|
21
|
+
scanners: vuln,misconfig
|
|
22
|
+
severity: HIGH,CRITICAL
|
|
23
|
+
format: sarif
|
|
24
|
+
output: trivy-results.sarif
|
|
25
|
+
exit-code: 0
|
|
26
|
+
- name: Upload SARIF to GitHub code scanning
|
|
27
|
+
if: always()
|
|
28
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
29
|
+
with:
|
|
30
|
+
sarif_file: trivy-results.sarif
|
|
31
|
+
- name: Fail on HIGH/CRITICAL findings
|
|
32
|
+
uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0
|
|
33
|
+
with:
|
|
34
|
+
scan-type: fs
|
|
35
|
+
scan-ref: .
|
|
36
|
+
scanners: vuln,misconfig
|
|
37
|
+
severity: HIGH,CRITICAL
|
|
38
|
+
format: table
|
|
39
|
+
exit-code: 1
|
|
40
|
+
skip-setup-trivy: true
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Trivy Vulnerability & Misconfiguration Scanning
|
|
2
|
+
|
|
3
|
+
The `vuln-scan` stage runs [Trivy](https://trivy.dev) against the agent's
|
|
4
|
+
worktree: known CVEs in dependency lockfiles (`vuln` scanner) and IaC/config
|
|
5
|
+
misconfigurations in Terraform, Dockerfiles, Kubernetes manifests, Helm charts,
|
|
6
|
+
and CloudFormation (`misconfig` scanner). Full verify (`verify --full`) runs it
|
|
7
|
+
automatically.
|
|
8
|
+
|
|
9
|
+
Trivy absorbed **tfsec** — its Terraform checks now live in Trivy's `misconfig`
|
|
10
|
+
scanner, so this plugin covers the Terraform/IaC use case without a separate tool.
|
|
11
|
+
|
|
12
|
+
## What runs
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
trivy fs --scanners vuln,misconfig --severity HIGH,CRITICAL --exit-code 1 \
|
|
16
|
+
--format json --output .har/artifacts/vuln-scan/report.json <work-dir>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The stage **fails** when any finding at or above the severity threshold exists.
|
|
20
|
+
Findings below the threshold are not reported at all — raise or lower the bar
|
|
21
|
+
via configuration.
|
|
22
|
+
|
|
23
|
+
## Configuration
|
|
24
|
+
|
|
25
|
+
Set in `harness.env` (or export before running):
|
|
26
|
+
|
|
27
|
+
| Variable | Default | Purpose |
|
|
28
|
+
|----------|---------|---------|
|
|
29
|
+
| `HARNESS_TRIVY_SEVERITY` | `HIGH,CRITICAL` | Comma-separated severities that fail the stage (`UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL`) |
|
|
30
|
+
| `HARNESS_TRIVY_SCANNERS` | `vuln,misconfig` | Scanners to run (`vuln`, `misconfig`, `secret`, `license`) |
|
|
31
|
+
| `TRIVY_CACHE_DIR` | `~/.cache/trivy` | Vulnerability DB cache — kept **outside** the worktrees so every slot shares one DB download (~60MB, refreshed every ~6h) |
|
|
32
|
+
|
|
33
|
+
## Artifacts
|
|
34
|
+
|
|
35
|
+
| Path | Contents |
|
|
36
|
+
|------|----------|
|
|
37
|
+
| `.har/artifacts/vuln-scan/report.json` | Full Trivy JSON report (machine-readable) |
|
|
38
|
+
| `.har/artifacts/vuln-scan/summary.txt` | Human-readable table (`trivy convert`) |
|
|
39
|
+
|
|
40
|
+
Artifacts always land in the **main repo root** `.har/artifacts/`, not the worktree.
|
|
41
|
+
|
|
42
|
+
## Suppressing findings
|
|
43
|
+
|
|
44
|
+
Add IDs to `.trivyignore` at the repo root — one CVE / check ID per line, no
|
|
45
|
+
trailing comments on the ID line:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
# Accepted risk: dev-only dependency, fix tracked in TICKET-123 (expires when bumped)
|
|
49
|
+
CVE-2021-23337
|
|
50
|
+
# Terraform check that conflicts with our architecture
|
|
51
|
+
AVD-AWS-0086
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The stage passes the **worktree's** copy via `--ignorefile`, so a suppression
|
|
55
|
+
travels with the change batch that introduces it and gets reviewed in the same
|
|
56
|
+
PR. Prefer fixing (bump the dependency, correct the config) over ignoring.
|
|
57
|
+
|
|
58
|
+
## Adaptations per repo
|
|
59
|
+
|
|
60
|
+
- **Container images**: the default stage scans the filesystem only. If the repo
|
|
61
|
+
builds an image, add a second invocation after the build:
|
|
62
|
+
`trivy image --severity "$SEVERITY" --exit-code 1 <image:tag>`.
|
|
63
|
+
- **Monorepos**: scope the scan by replacing `"$WORK_DIR"` with a subdirectory,
|
|
64
|
+
or add `--skip-dirs 'vendor/**,examples/**'`.
|
|
65
|
+
- **Lockfile-only scanning**: Trivy reads lockfiles (`package-lock.json`,
|
|
66
|
+
`yarn.lock`, `poetry.lock`, `go.sum`, `Cargo.lock`, …). Repos without a
|
|
67
|
+
lockfile get no dependency findings — commit one for meaningful results.
|
|
68
|
+
- **Secrets**: this harness pairs well with a dedicated secrets stage
|
|
69
|
+
(gitleaks plugin, which scans git history, not just the checkout). Add
|
|
70
|
+
`secret` to `HARNESS_TRIVY_SCANNERS` only if you don't run one.
|
|
71
|
+
|
|
72
|
+
## CI and compliance platforms (Vanta, Drata, …)
|
|
73
|
+
|
|
74
|
+
The local stage is **pre-merge shift-left** — it keeps findings from ever
|
|
75
|
+
reaching your default branch. Compliance platforms never see local runs.
|
|
76
|
+
|
|
77
|
+
The optional workflow `.github/workflows/trivy.yml` runs the same scan in CI and
|
|
78
|
+
uploads SARIF to **GitHub code scanning** (the repo's Security tab). That
|
|
79
|
+
org-level layer is what compliance platforms like Vanta ingest as evidence (via
|
|
80
|
+
their GitHub integration). Keep both: local stage to catch early, CI workflow to
|
|
81
|
+
produce the evidence.
|
|
82
|
+
|
|
83
|
+
## Running
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
./.har/stages/vuln-scan.sh 1 # standalone
|
|
87
|
+
./.har/verify.sh 1 --full # as part of full verification
|
|
88
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Trivy vulnerability + misconfiguration scan for an agent slot.
|
|
3
|
+
# Scans the agent's worktree filesystem: dependency lockfiles (vuln) and
|
|
4
|
+
# IaC/config files — Terraform, Dockerfile, Kubernetes, CloudFormation (misconfig).
|
|
5
|
+
# Outputs JSON to stdout, human-readable progress to stderr.
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./.har/stages/vuln-scan.sh <agent-id>
|
|
8
|
+
#
|
|
9
|
+
# Tune via harness.env or the environment:
|
|
10
|
+
# HARNESS_TRIVY_SEVERITY fail threshold (default: HIGH,CRITICAL)
|
|
11
|
+
# HARNESS_TRIVY_SCANNERS scanners to run (default: vuln,misconfig)
|
|
12
|
+
# TRIVY_CACHE_DIR vulnerability DB cache (default: ~/.cache/trivy —
|
|
13
|
+
# shared across slots so the DB downloads once)
|
|
14
|
+
#
|
|
15
|
+
# Prerequisite: ./.har/launch.sh <agent-id>
|
|
16
|
+
# See: ./.har/stages/TRIVY.md for the full adaptation guide.
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
HARNESS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
21
|
+
REPO_ROOT="$(cd "$HARNESS_DIR/.." && pwd)"
|
|
22
|
+
# agent-slot.sh expects SCRIPT_DIR to be .har/ (slot registry lives there)
|
|
23
|
+
SCRIPT_DIR="$HARNESS_DIR"
|
|
24
|
+
|
|
25
|
+
# shellcheck source=/dev/null
|
|
26
|
+
source "$HARNESS_DIR/harness.env"
|
|
27
|
+
# shellcheck source=/dev/null
|
|
28
|
+
source "$HARNESS_DIR/agent-slot.sh"
|
|
29
|
+
|
|
30
|
+
AGENT_ID="${1:?Usage: vuln-scan.sh <agent-id>}"
|
|
31
|
+
|
|
32
|
+
validate_agent_id "$AGENT_ID"
|
|
33
|
+
|
|
34
|
+
log() { echo "==> [vuln-scan agent-$AGENT_ID] $*" >&2; }
|
|
35
|
+
|
|
36
|
+
# ── Preflight ─────────────────────────────────────────────────────────────────
|
|
37
|
+
if ! command -v trivy >/dev/null 2>&1; then
|
|
38
|
+
echo "Error: trivy CLI not found." >&2
|
|
39
|
+
echo " Install: https://trivy.dev/latest/getting-started/installation/" >&2
|
|
40
|
+
echo " macOS: brew install trivy" >&2
|
|
41
|
+
echo " Linux: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b ~/.local/bin" >&2
|
|
42
|
+
echo " Then re-run: ./.har/stages/vuln-scan.sh ${AGENT_ID}" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# ── Resolve agent env ─────────────────────────────────────────────────────────
|
|
47
|
+
ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT")" || {
|
|
48
|
+
echo "No .env.agent.${AGENT_ID} found." >&2
|
|
49
|
+
har_suggest_launch "$AGENT_ID" >&2
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
set -a
|
|
54
|
+
# shellcheck source=/dev/null
|
|
55
|
+
source "$ENV_FILE"
|
|
56
|
+
set +a
|
|
57
|
+
|
|
58
|
+
WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
|
|
59
|
+
|
|
60
|
+
# ── Configuration ─────────────────────────────────────────────────────────────
|
|
61
|
+
SEVERITY="${HARNESS_TRIVY_SEVERITY:-HIGH,CRITICAL}"
|
|
62
|
+
SCANNERS="${HARNESS_TRIVY_SCANNERS:-vuln,misconfig}"
|
|
63
|
+
# One shared DB cache for every slot/worktree — first run downloads ~60MB,
|
|
64
|
+
# subsequent runs reuse it.
|
|
65
|
+
export TRIVY_CACHE_DIR="${TRIVY_CACHE_DIR:-$HOME/.cache/trivy}"
|
|
66
|
+
|
|
67
|
+
# ── Artifact directory ────────────────────────────────────────────────────────
|
|
68
|
+
ARTIFACT_DIR="$REPO_ROOT/.har/artifacts/vuln-scan"
|
|
69
|
+
mkdir -p "$ARTIFACT_DIR"
|
|
70
|
+
REPORT_JSON="$ARTIFACT_DIR/report.json"
|
|
71
|
+
|
|
72
|
+
# ── Scan ──────────────────────────────────────────────────────────────────────
|
|
73
|
+
# Use the WORKTREE's .trivyignore (not the main checkout's) so suppressions are
|
|
74
|
+
# reviewed with the code change that introduces them.
|
|
75
|
+
IGNOREFILE_ARGS=()
|
|
76
|
+
if [ -f "$WORK_DIR/.trivyignore" ]; then
|
|
77
|
+
IGNOREFILE_ARGS=(--ignorefile "$WORK_DIR/.trivyignore")
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
log "Scanning $WORK_DIR (scanners: $SCANNERS, fail on: $SEVERITY)..."
|
|
81
|
+
START=$(now_ms)
|
|
82
|
+
|
|
83
|
+
set +e
|
|
84
|
+
trivy fs \
|
|
85
|
+
--scanners "$SCANNERS" \
|
|
86
|
+
--severity "$SEVERITY" \
|
|
87
|
+
--exit-code 1 \
|
|
88
|
+
--format json \
|
|
89
|
+
--output "$REPORT_JSON" \
|
|
90
|
+
--no-progress \
|
|
91
|
+
${IGNOREFILE_ARGS[@]+"${IGNOREFILE_ARGS[@]}"} \
|
|
92
|
+
"$WORK_DIR" 2> >(sed 's/^/ /' >&2)
|
|
93
|
+
TRIVY_EXIT=$?
|
|
94
|
+
set -e
|
|
95
|
+
|
|
96
|
+
END=$(now_ms)
|
|
97
|
+
TOTAL_MS=$(( END - START ))
|
|
98
|
+
|
|
99
|
+
if [ ! -f "$REPORT_JSON" ]; then
|
|
100
|
+
echo "Error: trivy did not produce a report (exit $TRIVY_EXIT) — see stderr above." >&2
|
|
101
|
+
exit "$TRIVY_EXIT"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Human-readable summary next to the JSON report (best effort)
|
|
105
|
+
trivy convert --format table "$REPORT_JSON" > "$ARTIFACT_DIR/summary.txt" 2>/dev/null || true
|
|
106
|
+
|
|
107
|
+
# ── Output ────────────────────────────────────────────────────────────────────
|
|
108
|
+
STATUS="pass"
|
|
109
|
+
if [ "$TRIVY_EXIT" -ne 0 ]; then
|
|
110
|
+
STATUS="fail"
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
node -e "
|
|
114
|
+
const fs = require('fs');
|
|
115
|
+
const report = JSON.parse(fs.readFileSync('$REPORT_JSON', 'utf8'));
|
|
116
|
+
const results = report.Results || [];
|
|
117
|
+
let vulnerabilities = 0;
|
|
118
|
+
let misconfigurations = 0;
|
|
119
|
+
let secrets = 0;
|
|
120
|
+
const findings = [];
|
|
121
|
+
for (const r of results) {
|
|
122
|
+
for (const v of r.Vulnerabilities || []) {
|
|
123
|
+
vulnerabilities++;
|
|
124
|
+
findings.push({ type: 'vuln', target: r.Target, id: v.VulnerabilityID, pkg: v.PkgName, installed: v.InstalledVersion, fixed: v.FixedVersion || null, severity: v.Severity, title: (v.Title || '').slice(0, 120) });
|
|
125
|
+
}
|
|
126
|
+
for (const m of r.Misconfigurations || []) {
|
|
127
|
+
misconfigurations++;
|
|
128
|
+
findings.push({ type: 'misconfig', target: r.Target, id: m.ID, severity: m.Severity, title: (m.Title || '').slice(0, 120) });
|
|
129
|
+
}
|
|
130
|
+
for (const s of r.Secrets || []) {
|
|
131
|
+
secrets++;
|
|
132
|
+
findings.push({ type: 'secret', target: r.Target, id: s.RuleID, severity: s.Severity, title: (s.Title || '').slice(0, 120) });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const out = {
|
|
136
|
+
status: '$STATUS',
|
|
137
|
+
stageId: 'vuln-scan',
|
|
138
|
+
kind: 'test',
|
|
139
|
+
agent_id: $AGENT_ID,
|
|
140
|
+
total_ms: $TOTAL_MS,
|
|
141
|
+
severity_threshold: '$SEVERITY',
|
|
142
|
+
scanners: '$SCANNERS',
|
|
143
|
+
counts: { vulnerabilities, misconfigurations, secrets },
|
|
144
|
+
findings: findings.slice(0, 50),
|
|
145
|
+
artifacts: [
|
|
146
|
+
{ path: '.har/artifacts/vuln-scan/report.json', kind: 'report' },
|
|
147
|
+
{ path: '.har/artifacts/vuln-scan/summary.txt', kind: 'report' }
|
|
148
|
+
]
|
|
149
|
+
};
|
|
150
|
+
process.stdout.write(JSON.stringify(out, null, 2) + '\n');
|
|
151
|
+
"
|
|
152
|
+
|
|
153
|
+
if [ "$STATUS" = "fail" ]; then
|
|
154
|
+
log "✗ Findings at or above $SEVERITY — see .har/artifacts/vuln-scan/summary.txt"
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
157
|
+
log "✓ No findings at or above $SEVERITY (${TOTAL_MS}ms)"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# .trivyignore — suppress specific Trivy findings (one ID per line, no trailing
|
|
2
|
+
# comments). Prefer fixing over ignoring. Document every entry with a comment
|
|
3
|
+
# line above it saying why and when it can go.
|
|
4
|
+
#
|
|
5
|
+
# Example:
|
|
6
|
+
# # dev-only dependency, fix tracked in TICKET-123
|
|
7
|
+
# CVE-2021-23337
|
|
8
|
+
# # S3 public access is intentional for the www bucket
|
|
9
|
+
# AVD-AWS-0086
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Trivy plugin
|
|
2
|
+
|
|
3
|
+
Installs a `vuln-scan` stage that runs [Trivy](https://trivy.dev) against the
|
|
4
|
+
agent's worktree:
|
|
5
|
+
|
|
6
|
+
- **Dependency vulnerabilities** — known CVEs in lockfiles (`package-lock.json`,
|
|
7
|
+
`yarn.lock`, `poetry.lock`, `go.sum`, `Cargo.lock`, …)
|
|
8
|
+
- **IaC misconfigurations** — Terraform, Dockerfile, Kubernetes, Helm,
|
|
9
|
+
CloudFormation (Trivy absorbed tfsec, so this covers the Terraform use case)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
har env add-plugin trivy # --skip-ci to omit the GitHub workflow
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
What it adds:
|
|
16
|
+
|
|
17
|
+
- `.har/stages/vuln-scan.sh` — the stage, registered in `.har/stages.json` and
|
|
18
|
+
appended to `verificationStages` (runs on `verify --full`)
|
|
19
|
+
- `.har/stages/TRIVY.md` — adaptation guide (severity threshold, scanners,
|
|
20
|
+
container-image scanning, monorepo scoping)
|
|
21
|
+
- `.trivyignore` — suppression file scaffold
|
|
22
|
+
- `.github/workflows/trivy.yml` *(optional)* — same scan in CI with SARIF upload
|
|
23
|
+
to GitHub code scanning; that org-level layer is what compliance platforms
|
|
24
|
+
like Vanta ingest as evidence
|
|
25
|
+
|
|
26
|
+
External requirement: the `trivy` binary (`brew install trivy` or the
|
|
27
|
+
[install script](https://trivy.dev/latest/getting-started/installation/)).
|
|
28
|
+
The stage fails fast with an install hint when it is missing.
|
|
29
|
+
|
|
30
|
+
Fail threshold defaults to `HIGH,CRITICAL` — override with
|
|
31
|
+
`HARNESS_TRIVY_SEVERITY` in `.har/harness.env`. Reports land in
|
|
32
|
+
`.har/artifacts/vuln-scan/`.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "trivy",
|
|
3
|
+
"stageId": "vuln-scan",
|
|
4
|
+
"verificationStages": [
|
|
5
|
+
"typecheck",
|
|
6
|
+
"unit-tests",
|
|
7
|
+
"lint",
|
|
8
|
+
"vuln-scan"
|
|
9
|
+
],
|
|
10
|
+
"stage": {
|
|
11
|
+
"id": "vuln-scan",
|
|
12
|
+
"kind": "test",
|
|
13
|
+
"description": "Trivy vulnerability + IaC misconfiguration scan (dependencies, Terraform, Dockerfile, K8s)",
|
|
14
|
+
"script": "stages/vuln-scan.sh",
|
|
15
|
+
"requiresAgentId": true,
|
|
16
|
+
"artifacts": [
|
|
17
|
+
{
|
|
18
|
+
"path": ".har/artifacts/vuln-scan",
|
|
19
|
+
"kind": "directory",
|
|
20
|
+
"description": "Trivy JSON report and human-readable summary"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"path": ".har/artifacts/vuln-scan/report.json",
|
|
24
|
+
"kind": "report"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
{ "src": ".har/stages/vuln-scan.sh", "dest": ".har/stages/vuln-scan.sh", "executable": true },
|
|
30
|
+
{ "src": ".har/stages/TRIVY.md", "dest": ".har/stages/TRIVY.md" },
|
|
31
|
+
{ "src": ".trivyignore", "dest": ".trivyignore" }
|
|
32
|
+
],
|
|
33
|
+
"optionalFiles": [
|
|
34
|
+
{ "src": ".github/workflows/trivy.yml", "dest": ".github/workflows/trivy.yml", "skipFlag": "skipCi" }
|
|
35
|
+
],
|
|
36
|
+
"nextSteps": [
|
|
37
|
+
"Install trivy: brew install trivy (or https://trivy.dev/latest/getting-started/installation/)",
|
|
38
|
+
"./.har/launch.sh 1",
|
|
39
|
+
"./.har/stages/vuln-scan.sh 1",
|
|
40
|
+
"Open .har/artifacts/vuln-scan/summary.txt for findings",
|
|
41
|
+
"Tune HARNESS_TRIVY_SEVERITY / HARNESS_TRIVY_SCANNERS in .har/harness.env — read .har/stages/TRIVY.md"
|
|
42
|
+
],
|
|
43
|
+
"docsPath": ".har/stages/TRIVY.md"
|
|
44
|
+
}
|