project-athena 0.4.0 → 0.5.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/CHANGELOG.md +31 -0
- package/README.md +10 -0
- package/dist/{activity-BKW7G4I5.js → activity-XCHKBZTG.js} +4 -4
- package/dist/{agents-DFSNEOP5.js → agents-HUJ4JXIB.js} +4 -4
- package/dist/{ai-ASL4FFQ7.js → ai-LGTLQKL4.js} +3 -3
- package/dist/{analyze-ZY3MXBOI.js → analyze-AP4XJT7D.js} +7 -7
- package/dist/{chunk-ZB7LC6FP.js → chunk-3KFOI2X4.js} +2 -2
- package/dist/{chunk-MPG3IEGB.js → chunk-44EGCEB2.js} +23 -14
- package/dist/{chunk-GGXPG6OE.js → chunk-4FETKOY3.js} +8 -7
- package/dist/chunk-4IIBVTBS.js +76 -0
- package/dist/{chunk-LEMPCNAY.js → chunk-56EXAGTM.js} +3 -3
- package/dist/{chunk-DZDI2HRT.js → chunk-6OSQMXUP.js} +5 -5
- package/dist/{chunk-OLPSEQS5.js → chunk-AKOI4VTD.js} +10 -1
- package/dist/{chunk-FVCDMQUQ.js → chunk-C25FDXOO.js} +133 -23
- package/dist/{chunk-7DZK6WSQ.js → chunk-DH5AGULY.js} +2 -2
- package/dist/{chunk-TDNZ2RYA.js → chunk-F65QINMR.js} +2 -2
- package/dist/{chunk-WVQC2EGO.js → chunk-GU3ZX2J7.js} +2 -2
- package/dist/{chunk-UAZLPNVC.js → chunk-GYQG4Q2N.js} +4 -4
- package/dist/chunk-IDODPGTX.js +595 -0
- package/dist/chunk-KJYSNZXS.js +934 -0
- package/dist/{chunk-3XSDI5WF.js → chunk-L2EHJCH4.js} +5 -5
- package/dist/{chunk-65TSRDQ3.js → chunk-P4TRQMES.js} +2 -2
- package/dist/{chunk-KHMBNT66.js → chunk-PZPMWCXS.js} +5 -5
- package/dist/{chunk-XDCKZ5TV.js → chunk-R5AX7VST.js} +2 -2
- package/dist/{chunk-QLW657NT.js → chunk-RQKGT2EK.js} +2 -2
- package/dist/{chunk-VSYBC3VY.js → chunk-UMWX4UTK.js} +2 -2
- package/dist/{chunk-RXNRMIZP.js → chunk-WUIC65QO.js} +2 -2
- package/dist/{chunk-JCGDR4UV.js → chunk-WWQ5NKEC.js} +3 -1
- package/dist/{chunk-RVTNSNGK.js → chunk-X7EYOAEX.js} +2 -2
- package/dist/chunk-Y6SDMZET.js +278 -0
- package/dist/{clean-MDBYH62P.js → clean-LQOG56AM.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/{context-PLPCAPCO.js → context-G5YHY3L2.js} +8 -8
- package/dist/{doctor-BX677KRE.js → doctor-L34QJ7WR.js} +8 -8
- package/dist/{event-TNT6CQZR.js → event-6YW6TGFI.js} +3 -3
- package/dist/{git-hook-YZNZGAAP.js → git-hook-Y7Y262F7.js} +2 -2
- package/dist/{init-WQCAOJ2R.js → init-427QR5AS.js} +7 -7
- package/dist/{mcp-U7EPH43L.js → mcp-DWNWRGJW.js} +51 -14
- package/dist/{memory-VSVNLEJC.js → memory-XBZCYQLW.js} +3 -3
- package/dist/{open-UJZRYF4E.js → open-LJMOB6U2.js} +33 -31
- package/dist/{program-CWJNGCTZ.js → program-DCP526LU.js} +46 -32
- package/dist/{review-OOKXSYMP.js → review-VEJBPACX.js} +10 -9
- package/dist/{rules-4FXJUCXB.js → rules-M4DLLXBL.js} +4 -4
- package/dist/scan-JVKCVHPD.js +939 -0
- package/dist/{security-LME3XFL3.js → security-JTJN3E7K.js} +13 -11
- package/dist/{staged-check-X24C3YRD.js → staged-check-PQSLGQBW.js} +11 -11
- package/dist/{status-RR57ZNGI.js → status-PO27BSOZ.js} +4 -4
- package/dist/{sync-KFJANRXF.js → sync-GVO2VHPI.js} +10 -10
- package/dist/{watch-CY7QHIYN.js → watch-TEHFO47V.js} +10 -10
- package/package.json +3 -1
- package/dist/chunk-SJ4DQFIJ.js +0 -379
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 — 2026-10-03
|
|
4
|
+
|
|
5
|
+
Unified findings: every security check (secrets, dependency audits, review checks) now reports into one findings model with value-free fingerprints, an honest coverage record, and a quality gate driven by an optional committed policy, baseline and triage. New `athena scan` writes text, JSON, Markdown, GitHub annotations or SARIF 2.1.0 for code scanning, and `--policy-from <base>` keeps a pull request from quietly weakening the gate. Static analysis rules arrive in 0.6.0; until then reports say plainly that SAST and IaC are not covered.
|
|
6
|
+
|
|
7
|
+
### `athena scan` and friends
|
|
8
|
+
|
|
9
|
+
- New `athena scan`: runs the secret scanner and dependency audits over the whole project, or with `--base <ref>` / `--staged` / `--changed` over a change (adding the review checks), then applies the policy, baseline, triage and inline suppressions and evaluates the quality gate. Exit 1 when the gate fails (`--no-fail` to report only), 2 when Git can't compute the requested change. `--only secrets,deps,review`, `--format text|json|markdown|github|sarif`, `-o <file>`, gate overrides `--fail-on`, `--min-confidence`, `--unrated`, plus `--no-baseline`, `--policy-from <ref>` and `--offline` (audits are skipped and reported as skipped). Full-project scans are saved to `.athena/findings.json`.
|
|
10
|
+
- New `athena baseline show|create|update|prune`, `athena findings list|show|triage` (fingerprint prefixes of 6+ characters; a reason is required and committed) and `athena explain <ruleId>` / `athena scan --list-rules`.
|
|
11
|
+
- New MCP tool `findings`: a read-only summary of the last scan (gate, ratings, coverage gaps, active findings with fingerprints), fenced as untrusted data and never containing secret values. It never starts a scan.
|
|
12
|
+
- `athena security` keeps its flags, output and `--json` shape; it now also writes `.athena/findings.json`.
|
|
13
|
+
|
|
14
|
+
### Unified findings: secrets, dependencies and review checks
|
|
15
|
+
|
|
16
|
+
- New `.athena/findings.json` (gitignored): every security check now reports into one findings model with stable, value-free fingerprints, severity (`critical`…`info`, plus `unrated`), confidence, a FACT/DETECTED/INFERRED label, CWE, location or package, the engine that reported it, and a coverage record per engine (files scanned and skipped, tools unavailable/failed/timed out/skipped offline).
|
|
17
|
+
- Secrets: one `secret/<pattern>` finding per match with file, line and column. Private keys and AWS/GCP/Azure credentials are `critical`, other known token formats `high`, keyword heuristics `medium` and marked *potential* (low confidence). Fingerprints hash the line with the matched value masked, so they survive rotating the value and reveal nothing about it; no secret value is ever written.
|
|
18
|
+
- Dependency audits: one `dependency/vulnerable-package` finding per package and advisory (FACT, attributed to `npm-audit`, `pnpm-audit`, `pip-audit`, `govulncheck`, `cargo-audit` or `composer-audit`), with version, lockfile/target, advisory ids, vulnerable range, fixed version and CWE where the tool reports them. `moderate` is now `medium`; a missing severity stays `unrated`. npm packages that are only vulnerable through another reported package are not counted twice.
|
|
19
|
+
- `athena review --json` gains a `scan` object (`scan.findings`, `scan.coverage`) with the checks as `review/<check>` findings (blocker → high, warning → medium, info → info) and added secrets as `secret/*` findings at the exact line. The existing `findings` list, human output and exit codes are unchanged.
|
|
20
|
+
- `athena security` runs on the new scanners and writes both `.athena/findings.json` and the legacy `.athena/security-scan.json` (still used by the web UI and `security.md`). Flags, output, `--fail-on` and `--unrated` behave as before; `--json` keeps the v1 shape — the unified shape will be `athena scan --format json`.
|
|
21
|
+
- A project that only has a `security-scan.json` from an older version is read as findings (its secret count becomes a coverage note, since v1 recorded no lines).
|
|
22
|
+
|
|
23
|
+
### Policy, baseline, triage and quality gate
|
|
24
|
+
|
|
25
|
+
- `.athena/policy.json` (optional, committed; `schemaVersion: 1`): `gate` (`failOn` severity, default `high`; `minConfidence`, default `medium`; `unrated: fail|warn`, default `fail`; `scope: new|all`, default `new`; gated `categories`, default secret, dependency, sast, iac, review), per-rule `rules` (`"off"` or a `severity`/`confidence` override; exact ids or `family/*` prefixes), `paths.exclude`/`paths.tests` globs, `licenses.deny`/`warn`, and `external` engines (`semgrep`, `gitleaks`, `trivy`, `osv-scanner`: `auto|off`; `network: allow|deny`). A missing file means defaults. An invalid file, including an unknown key, is an error that names the offending path. It is never silently ignored.
|
|
26
|
+
- `.athena/baseline.json` (committed): records findings that already exist so that only new ones fail the gate (scope `new`). Baselined findings stay visible and still count in ratings. Entries hold only value-free fingerprints, rule ids and files. Pruning needs a full scan and keeps entries of any category whose engine failed or did not run, because a broken engine reports nothing and that must not read as "fixed".
|
|
27
|
+
- `.athena/triage.json` (committed): a reviewed decision per finding (`safe`, `false-positive`, `accepted-risk`, `fixed`, `to-review`), similar to SonarQube's hotspot review. Every decision needs a reason (at most 500 characters). A reason that looks like it contains a secret is refused, and the value is never echoed. `to-review` keeps the finding open.
|
|
28
|
+
- Inline suppressions: `athena-ignore <ruleId> -- <reason>` in a `//`, `#`, `/* */`, `<!-- -->` or `--` comment on the finding's line or the line above. Each comment names one exact rule. A suppression without a reason is ignored and reported as an `info` finding (`review/suppression-without-reason`). A comment with no valid rule id is reported as `review/invalid-suppression`.
|
|
29
|
+
- Every finding gets a status on each run (never stored): `open`, `baselined`, `suppressed`, `triaged:<safe|false-positive|accepted-risk|fixed>` or `excluded`.
|
|
30
|
+
- Quality gate: fails on active findings in gated categories at or above `failOn` with at least `minConfidence` confidence. Unrated findings follow `gate.unrated`. With scope `new`, baselined findings are skipped. The gate also fails when an engine for a gated category failed or timed out, and the result says so explicitly, because an engine that broke checked nothing. Unavailable or skipped engines, and gated categories that nothing scanned, are listed as warnings.
|
|
31
|
+
- Ratings A–E per category, based on the worst active finding: A none, B low/info, C medium, D high, E critical. Unrated counts as D, or C under `unrated: warn`. Each rating comes with a basis ("N rules across <languages>; X files not covered") and no numeric score. A category is not rated when no engine ran for it or one of its engines failed.
|
|
32
|
+
- Anti-tamper for CI (`policyFrom: <ref>`): the gate uses the policy, baseline and triage committed at the base ref (missing there means defaults). Every change to them since `merge-base(ref, HEAD)`, and every added `athena-ignore` comment (tracked or untracked), is reported as `review/policy-weakened`. Severity is `high` when the change relaxes the gate (a higher `failOn` or `minConfidence`, rules turned off or downgraded, new excludes, fewer gated categories, engines turned off, added baseline entries, new closing triage decisions, added suppressions); other changes are `info`. A base-branch policy can downgrade this rule if a team prefers review notes over a failing check.
|
|
33
|
+
|
|
3
34
|
## 0.4.0 — 2026-10-03
|
|
4
35
|
|
|
5
36
|
Agent memory: coding agents can now record what they learn — decisions and why, gotchas, bug root causes, conventions — and later sessions recall it. Memory lives in `.athena/memory/` as plain Markdown you commit and review like code. Anything an agent writes is `unreviewed` (INFERRED) until you confirm it (`athena memory review`, or the Memory page in `athena open`); entries turn `stale` when the files they describe change. Secrets are refused, and text that looks like it is trying to instruct the agent is flagged and kept out of recall.
|
package/README.md
CHANGED
|
@@ -62,6 +62,10 @@ Generated content sits between `athena:generated` markers. Anything outside the
|
|
|
62
62
|
| `athena rules` | `list`, `add "<rule>" --section <name>`, `edit`, `enable`, `disable`, `remove` |
|
|
63
63
|
| `athena agents` | `list`, `add <claude-code\|cursor\|codex\|copilot\|gemini-cli\|antigravity\|windsurf\|cline\|agents-md\|all>`, `remove` |
|
|
64
64
|
| `athena activity` | Show AI agent activity observed through hooks (`-n`, `--agent`) |
|
|
65
|
+
| `athena scan` | Unified scan: secrets, dependency audits and (on a change) review checks, with policy, baseline, triage and a quality gate (`--base`, `--staged`, `--changed`, `--only`, `-f text\|json\|markdown\|github\|sarif`, `-o`, `--fail-on`, `--policy-from`, `--offline`, `--list-rules`) |
|
|
66
|
+
| `athena baseline` | `show`, `create`, `update`, `prune` — record existing findings so only new ones fail the gate |
|
|
67
|
+
| `athena findings` | `list`, `show <fingerprint>`, `triage <fingerprint> <safe\|false-positive\|accepted-risk\|fixed\|to-review\|clear> --reason …` |
|
|
68
|
+
| `athena explain <ruleId>` | What a rule detects and what to do about it |
|
|
65
69
|
| `athena security` | Audit dependencies with the tools installed for this project, and report secret findings (`--fail-on`, `--last`, `--no-audit`) |
|
|
66
70
|
| `athena review` | Check the current diff for facts worth reviewing, and list your rules and checklist (`--base`, `--no-fail`) |
|
|
67
71
|
| `athena git-hook` | `install` (`--review`), `uninstall`, `status` — a Git pre-commit hook that blocks commits when knowledge is out of date |
|
|
@@ -170,6 +174,10 @@ $ athena activity
|
|
|
170
174
|
## Security and review
|
|
171
175
|
|
|
172
176
|
```bash
|
|
177
|
+
athena scan # secrets + dependency audits, policy and quality gate
|
|
178
|
+
athena scan --base origin/main -f sarif -o athena.sarif --policy-from origin/main # in CI
|
|
179
|
+
athena baseline create # accept today's findings; only new ones fail the gate
|
|
180
|
+
athena findings triage 5f65caf9 false-positive --reason "test fixture"
|
|
173
181
|
athena security # audit dependencies + report secret findings
|
|
174
182
|
athena security --fail-on high # exit 1 in CI
|
|
175
183
|
athena review # check the current diff before you commit
|
|
@@ -177,6 +185,8 @@ athena review # check the current diff before you commit
|
|
|
177
185
|
|
|
178
186
|
**`athena security`** runs the audit tools your project's ecosystems provide (`npm audit`, `pnpm audit`, `pip-audit`, `govulncheck`, `cargo audit`, `composer audit`) and reports what they find, attributed to the tool. Athena has no vulnerability database of its own: a tool that isn't installed is reported as **unknown**, never as "no problems". Results are stored in `.athena/security-scan.json` and recorded in `security.md` on the next `athena sync`.
|
|
179
187
|
|
|
188
|
+
**`athena scan`** puts every check into one findings model. Each finding has a rule id, severity (`unrated` when the tool gave none — never treated as low), confidence, a FACT/DETECTED/INFERRED label, CWE, a location or package, and a fingerprint that never depends on a secret's value. Every report lists what was *not* covered (tools not installed, engines that failed, categories nothing scanned), and a failed engine fails the gate — it checked nothing. Optional committed files tune it: `.athena/policy.json` (gate thresholds, rule overrides, path excludes), `.athena/baseline.json`, `.athena/triage.json`, and inline `athena-ignore <ruleId> -- <reason>` comments (a reason is required). In CI, `--policy-from <base>` evaluates with the base branch's policy and reports any change to it on the branch as `review/policy-weakened`. Output formats: text, JSON, Markdown, GitHub annotations and SARIF 2.1.0 (for code scanning). Ratings A–E per category come with their basis; there is no numeric score.
|
|
189
|
+
|
|
180
190
|
**`athena review`** checks facts about your current diff: secrets in added lines (a blocker, exit 1), committed env files, new dependencies, source changed without tests, API/schema/auth touchpoints, large files, debug leftovers, and whether Athena knowledge is stale. It then lists your enabled rules and the project's review checklist for you or your agent to apply — Athena does not claim to judge whether they are met, and it runs no AI.
|
|
181
191
|
|
|
182
192
|
## Use in CI and git hooks
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
listAgents
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RQKGT2EK.js";
|
|
5
5
|
import {
|
|
6
6
|
readRecentEvents,
|
|
7
7
|
summarizeSessions
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-P4TRQMES.js";
|
|
9
9
|
import {
|
|
10
10
|
c,
|
|
11
11
|
dim,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
requireProjectRoot
|
|
21
21
|
} from "./chunk-XFN7UZCS.js";
|
|
22
22
|
import "./chunk-KBO7XRBW.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-WWQ5NKEC.js";
|
|
24
24
|
import "./chunk-YRIS7ZS6.js";
|
|
25
25
|
import "./chunk-GHFRQAYT.js";
|
|
26
26
|
import "./chunk-INT3IGMQ.js";
|
|
@@ -74,4 +74,4 @@ async function activityCommand(opts) {
|
|
|
74
74
|
export {
|
|
75
75
|
activityCommand
|
|
76
76
|
};
|
|
77
|
-
//# sourceMappingURL=activity-
|
|
77
|
+
//# sourceMappingURL=activity-XCHKBZTG.js.map
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
configureAgents,
|
|
4
4
|
listAgents,
|
|
5
5
|
removeAgents
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-RQKGT2EK.js";
|
|
7
|
+
import "./chunk-P4TRQMES.js";
|
|
8
8
|
import {
|
|
9
9
|
c,
|
|
10
10
|
dim,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
requireProjectRoot
|
|
20
20
|
} from "./chunk-XFN7UZCS.js";
|
|
21
21
|
import "./chunk-KBO7XRBW.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-WWQ5NKEC.js";
|
|
23
23
|
import "./chunk-YRIS7ZS6.js";
|
|
24
24
|
import "./chunk-GHFRQAYT.js";
|
|
25
25
|
import "./chunk-INT3IGMQ.js";
|
|
@@ -66,4 +66,4 @@ export {
|
|
|
66
66
|
agentsListCommand,
|
|
67
67
|
agentsRemoveCommand
|
|
68
68
|
};
|
|
69
|
-
//# sourceMappingURL=agents-
|
|
69
|
+
//# sourceMappingURL=agents-HUJ4JXIB.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
aiStatus,
|
|
4
4
|
buildEnrichPayload,
|
|
5
5
|
enrich
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WUIC65QO.js";
|
|
7
7
|
import {
|
|
8
8
|
c,
|
|
9
9
|
dim,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./chunk-XFN7UZCS.js";
|
|
21
21
|
import "./chunk-KBO7XRBW.js";
|
|
22
22
|
import "./chunk-PVCIGUIT.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-WWQ5NKEC.js";
|
|
24
24
|
import "./chunk-FQEGEVLY.js";
|
|
25
25
|
import "./chunk-2DY4PUR4.js";
|
|
26
26
|
import "./chunk-IQQBAWSM.js";
|
|
@@ -93,4 +93,4 @@ export {
|
|
|
93
93
|
aiEnrichCommand,
|
|
94
94
|
aiStatusCommand
|
|
95
95
|
};
|
|
96
|
-
//# sourceMappingURL=ai-
|
|
96
|
+
//# sourceMappingURL=ai-LGTLQKL4.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
printSummary,
|
|
5
5
|
runWithProgress,
|
|
6
6
|
summarize
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-R5AX7VST.js";
|
|
8
8
|
import {
|
|
9
9
|
printHeader
|
|
10
10
|
} from "./chunk-SHKXELUX.js";
|
|
@@ -16,14 +16,14 @@ import {
|
|
|
16
16
|
requireProjectRoot
|
|
17
17
|
} from "./chunk-XFN7UZCS.js";
|
|
18
18
|
import "./chunk-KBO7XRBW.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-MPG3IEGB.js";
|
|
21
|
-
import "./chunk-ELKFRTZT.js";
|
|
19
|
+
import "./chunk-4FETKOY3.js";
|
|
22
20
|
import "./chunk-2R6PXY6Z.js";
|
|
21
|
+
import "./chunk-44EGCEB2.js";
|
|
23
22
|
import "./chunk-PVCIGUIT.js";
|
|
24
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-ELKFRTZT.js";
|
|
24
|
+
import "./chunk-AKOI4VTD.js";
|
|
25
25
|
import "./chunk-QCC4VJUH.js";
|
|
26
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-WWQ5NKEC.js";
|
|
27
27
|
import "./chunk-YRIS7ZS6.js";
|
|
28
28
|
import "./chunk-GHFRQAYT.js";
|
|
29
29
|
import "./chunk-INT3IGMQ.js";
|
|
@@ -47,4 +47,4 @@ async function analyzeCommand(opts) {
|
|
|
47
47
|
export {
|
|
48
48
|
analyzeCommand
|
|
49
49
|
};
|
|
50
|
-
//# sourceMappingURL=analyze-
|
|
50
|
+
//# sourceMappingURL=analyze-AP4XJT7D.js.map
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
git,
|
|
14
14
|
statusV2,
|
|
15
15
|
workingChanges
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AKOI4VTD.js";
|
|
17
17
|
import {
|
|
18
18
|
isBlockModified,
|
|
19
19
|
parseBlocks
|
|
@@ -218,4 +218,4 @@ async function buildStatus(root, signal, opts = {}) {
|
|
|
218
218
|
export {
|
|
219
219
|
buildStatus
|
|
220
220
|
};
|
|
221
|
-
//# sourceMappingURL=chunk-
|
|
221
|
+
//# sourceMappingURL=chunk-3KFOI2X4.js.map
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getOrCreateSalt,
|
|
4
|
+
keyedFingerprint,
|
|
5
|
+
readLocalConfig
|
|
6
|
+
} from "./chunk-PVCIGUIT.js";
|
|
2
7
|
import {
|
|
3
8
|
IgnoreMatcher,
|
|
4
9
|
SOURCE_FILE,
|
|
@@ -8,14 +13,9 @@ import {
|
|
|
8
13
|
sniffBinary,
|
|
9
14
|
walkProject
|
|
10
15
|
} from "./chunk-ELKFRTZT.js";
|
|
11
|
-
import {
|
|
12
|
-
getOrCreateSalt,
|
|
13
|
-
keyedFingerprint,
|
|
14
|
-
readLocalConfig
|
|
15
|
-
} from "./chunk-PVCIGUIT.js";
|
|
16
16
|
import {
|
|
17
17
|
gitInfo
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AKOI4VTD.js";
|
|
19
19
|
import {
|
|
20
20
|
ProjectModel,
|
|
21
21
|
detected,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
isSecretLikeName,
|
|
29
29
|
redact,
|
|
30
30
|
scanText
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-WWQ5NKEC.js";
|
|
32
32
|
import {
|
|
33
33
|
FactsCache,
|
|
34
34
|
athenaDir,
|
|
@@ -2379,9 +2379,6 @@ import path4 from "path";
|
|
|
2379
2379
|
import crypto2 from "crypto";
|
|
2380
2380
|
var SKIP = /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lock|poetry\.lock|uv\.lock|Cargo\.lock|go\.sum|composer\.lock|Gemfile\.lock|pubspec\.lock)$|\.(min\.js|map|svg|snap)$/;
|
|
2381
2381
|
var MAX_SECRET_FINDINGS = 500;
|
|
2382
|
-
async function secretFingerprinter(root) {
|
|
2383
|
-
return (await secretFingerprintKey(root)).fingerprint;
|
|
2384
|
-
}
|
|
2385
2382
|
async function secretFingerprintKey(root, saltOverride) {
|
|
2386
2383
|
try {
|
|
2387
2384
|
let salt = saltOverride;
|
|
@@ -2398,12 +2395,20 @@ async function secretFingerprintKey(root, saltOverride) {
|
|
|
2398
2395
|
}
|
|
2399
2396
|
return { fingerprint: ephemeralFingerprint, fingerprintKeyId: null };
|
|
2400
2397
|
}
|
|
2398
|
+
var MINIFIED = /\.(min\.js|map)$/;
|
|
2401
2399
|
async function scanFilesForSecrets(files, read, opts) {
|
|
2402
2400
|
const findings = opts.into ?? [];
|
|
2403
2401
|
const max = opts.max ?? MAX_SECRET_FINDINGS;
|
|
2404
2402
|
const stats = { skippedLongLines: 0 };
|
|
2403
|
+
const counts = { scanned: 0, binary: 0, tooLarge: 0, minified: 0, excluded: 0, unreadable: 0 };
|
|
2405
2404
|
let truncated = false;
|
|
2406
2405
|
for (const f of files) {
|
|
2406
|
+
if (f.binary) counts.binary++;
|
|
2407
|
+
else if (f.large) counts.tooLarge++;
|
|
2408
|
+
else if (SKIP.test(f.path)) {
|
|
2409
|
+
if (MINIFIED.test(f.path)) counts.minified++;
|
|
2410
|
+
else counts.excluded++;
|
|
2411
|
+
}
|
|
2407
2412
|
if (f.binary || f.large || SKIP.test(f.path)) continue;
|
|
2408
2413
|
if (findings.length >= max) {
|
|
2409
2414
|
truncated = true;
|
|
@@ -2411,10 +2416,14 @@ async function scanFilesForSecrets(files, read, opts) {
|
|
|
2411
2416
|
}
|
|
2412
2417
|
opts.signal?.throwIfAborted();
|
|
2413
2418
|
const text = await read(f.path);
|
|
2419
|
+
if (text === null) counts.unreadable++;
|
|
2420
|
+
else counts.scanned++;
|
|
2414
2421
|
if (!text) continue;
|
|
2415
|
-
|
|
2422
|
+
const matches = scanText(text, { fingerprint: opts.fingerprint, stats });
|
|
2423
|
+
for (const m of matches) findings.push({ type: m.type, file: f.path, line: m.line, fingerprint: m.fingerprint });
|
|
2424
|
+
if (matches.length) opts.onFile?.(f.path, text, matches);
|
|
2416
2425
|
}
|
|
2417
|
-
return { findings, truncated, skippedLongLines: stats.skippedLongLines };
|
|
2426
|
+
return { findings, truncated, skippedLongLines: stats.skippedLongLines, files: counts };
|
|
2418
2427
|
}
|
|
2419
2428
|
var secretsFact = {
|
|
2420
2429
|
id: "secrets",
|
|
@@ -2673,7 +2682,7 @@ export {
|
|
|
2673
2682
|
readExclude,
|
|
2674
2683
|
manifestDependencyNames,
|
|
2675
2684
|
REAL_ENV_NAMES,
|
|
2676
|
-
|
|
2685
|
+
MAX_SECRET_FINDINGS,
|
|
2677
2686
|
scanFilesForSecrets,
|
|
2678
2687
|
analyzeProject,
|
|
2679
2688
|
persistAnalysisCache,
|
|
@@ -2688,4 +2697,4 @@ export {
|
|
|
2688
2697
|
unratedFindings,
|
|
2689
2698
|
meetsThreshold
|
|
2690
2699
|
};
|
|
2691
|
-
//# sourceMappingURL=chunk-
|
|
2700
|
+
//# sourceMappingURL=chunk-44EGCEB2.js.map
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ATHENA_VERSION
|
|
4
|
+
} from "./chunk-2R6PXY6Z.js";
|
|
2
5
|
import {
|
|
3
6
|
analyzeProject,
|
|
4
7
|
loadScanFromDir,
|
|
5
8
|
persistAnalysisCache
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
ATHENA_VERSION
|
|
9
|
-
} from "./chunk-2R6PXY6Z.js";
|
|
9
|
+
} from "./chunk-44EGCEB2.js";
|
|
10
10
|
import {
|
|
11
11
|
LOCAL_CONFIG_FILE
|
|
12
12
|
} from "./chunk-PVCIGUIT.js";
|
|
13
13
|
import {
|
|
14
14
|
redact
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WWQ5NKEC.js";
|
|
16
16
|
import {
|
|
17
17
|
ADAPTERS,
|
|
18
18
|
applyChanges
|
|
@@ -752,6 +752,7 @@ var ATHENA_GITIGNORE = [
|
|
|
752
752
|
"model.json",
|
|
753
753
|
"graph.json",
|
|
754
754
|
"security-scan.json",
|
|
755
|
+
"findings.json",
|
|
755
756
|
"ai-suggestions.md",
|
|
756
757
|
".backup/",
|
|
757
758
|
".server.json",
|
|
@@ -764,7 +765,7 @@ var ATHENA_GITIGNORE = [
|
|
|
764
765
|
"local.json",
|
|
765
766
|
""
|
|
766
767
|
].join("\n");
|
|
767
|
-
var GITIGNORE_ENTRIES = ["state.json", "model.json", "graph.json", "security-scan.json", "ai-suggestions.md", ".backup/", ".server.json", ".sync-ignore.json", ".agent-events.jsonl", ".agent-events.1.jsonl", ".agent-events.rotate.lock", "cache/", "local.json"];
|
|
768
|
+
var GITIGNORE_ENTRIES = ["state.json", "model.json", "graph.json", "security-scan.json", "findings.json", "ai-suggestions.md", ".backup/", ".server.json", ".sync-ignore.json", ".agent-events.jsonl", ".agent-events.1.jsonl", ".agent-events.rotate.lock", "cache/", "local.json"];
|
|
768
769
|
async function ensureGitignoreEntries(file) {
|
|
769
770
|
const existing = await readTextIfExists(file);
|
|
770
771
|
if (existing === null) {
|
|
@@ -901,4 +902,4 @@ export {
|
|
|
901
902
|
composeState,
|
|
902
903
|
runPipeline
|
|
903
904
|
};
|
|
904
|
-
//# sourceMappingURL=chunk-
|
|
905
|
+
//# sourceMappingURL=chunk-4FETKOY3.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
auditDependencies,
|
|
4
|
+
legacyToolResult,
|
|
5
|
+
saveFindings
|
|
6
|
+
} from "./chunk-IDODPGTX.js";
|
|
7
|
+
import {
|
|
8
|
+
Coverage,
|
|
9
|
+
Finding,
|
|
10
|
+
dedupeFindings,
|
|
11
|
+
scanSecrets
|
|
12
|
+
} from "./chunk-Y6SDMZET.js";
|
|
13
|
+
import {
|
|
14
|
+
saveScan
|
|
15
|
+
} from "./chunk-44EGCEB2.js";
|
|
16
|
+
import {
|
|
17
|
+
walkProject
|
|
18
|
+
} from "./chunk-ELKFRTZT.js";
|
|
19
|
+
import {
|
|
20
|
+
loadConfig
|
|
21
|
+
} from "./chunk-2DY4PUR4.js";
|
|
22
|
+
|
|
23
|
+
// src/services/security.ts
|
|
24
|
+
var resultsByScan = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
async function runSecurityScanWithFindings(root, model, opts = {}) {
|
|
26
|
+
const started = Date.now();
|
|
27
|
+
const { config } = await loadConfig(root);
|
|
28
|
+
const walk = await walkProject(root, { config, signal: opts.signal });
|
|
29
|
+
const ctx = { root, config, mode: "all", signal: opts.signal };
|
|
30
|
+
const deps = opts.skipAudit ? null : await auditDependencies(ctx, { model, walk: walk.files, timeoutMs: opts.timeoutMs, onTool: opts.onTool });
|
|
31
|
+
const tools = (deps?.runs ?? []).map(legacyToolResult);
|
|
32
|
+
const counts = { critical: 0, high: 0, moderate: 0, low: 0, unrated: 0 };
|
|
33
|
+
for (const t of tools) for (const f of t.findings) counts[f.severity]++;
|
|
34
|
+
const secrets = await scanSecrets(ctx, { walk: walk.files });
|
|
35
|
+
const scannedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
36
|
+
const durationMs = Date.now() - started;
|
|
37
|
+
const scan = {
|
|
38
|
+
schemaVersion: 1,
|
|
39
|
+
scannedAt,
|
|
40
|
+
durationMs,
|
|
41
|
+
tools,
|
|
42
|
+
counts,
|
|
43
|
+
secrets: {
|
|
44
|
+
count: secrets.summary.count,
|
|
45
|
+
files: secrets.summary.files.slice(0, 50),
|
|
46
|
+
skippedLongLines: secrets.summary.skippedLongLines,
|
|
47
|
+
...secrets.summary.truncated ? { truncated: true } : {}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const outputs = [secrets, ...deps ? [deps] : []];
|
|
51
|
+
const result = {
|
|
52
|
+
schemaVersion: 1,
|
|
53
|
+
scannedAt,
|
|
54
|
+
durationMs,
|
|
55
|
+
scope: { mode: "all" },
|
|
56
|
+
findings: dedupeFindings(outputs.flatMap((o) => o.findings.map((f) => Finding.parse(f)))),
|
|
57
|
+
coverage: [...deps?.coverage ?? [], ...secrets.coverage].map((c) => Coverage.parse(c))
|
|
58
|
+
};
|
|
59
|
+
resultsByScan.set(scan, result);
|
|
60
|
+
return { scan, result };
|
|
61
|
+
}
|
|
62
|
+
async function runSecurityScan(root, model, opts = {}) {
|
|
63
|
+
return (await runSecurityScanWithFindings(root, model, opts)).scan;
|
|
64
|
+
}
|
|
65
|
+
async function saveScan2(root, scan) {
|
|
66
|
+
await saveScan(root, scan);
|
|
67
|
+
const result = resultsByScan.get(scan);
|
|
68
|
+
if (result) await saveFindings(root, result);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
runSecurityScanWithFindings,
|
|
73
|
+
runSecurityScan,
|
|
74
|
+
saveScan2 as saveScan
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=chunk-4IIBVTBS.js.map
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from "./chunk-3IS2X2Z7.js";
|
|
10
10
|
import {
|
|
11
11
|
git
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-AKOI4VTD.js";
|
|
13
13
|
import {
|
|
14
14
|
scanText
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WWQ5NKEC.js";
|
|
16
16
|
import {
|
|
17
17
|
isBlockModified,
|
|
18
18
|
parseBlocks
|
|
@@ -206,4 +206,4 @@ export {
|
|
|
206
206
|
rulesUpdate,
|
|
207
207
|
rulesRemove
|
|
208
208
|
};
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
209
|
+
//# sourceMappingURL=chunk-56EXAGTM.js.map
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
AnalysisScheduler
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-X7EYOAEX.js";
|
|
5
5
|
import {
|
|
6
6
|
applySync,
|
|
7
7
|
needsIndexRefreshOnly
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PZPMWCXS.js";
|
|
9
9
|
import {
|
|
10
10
|
readExclude
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-44EGCEB2.js";
|
|
12
12
|
import {
|
|
13
13
|
IgnoreMatcher,
|
|
14
14
|
factsConfigHash
|
|
15
15
|
} from "./chunk-ELKFRTZT.js";
|
|
16
16
|
import {
|
|
17
17
|
git
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AKOI4VTD.js";
|
|
19
19
|
import {
|
|
20
20
|
loadConfig
|
|
21
21
|
} from "./chunk-2DY4PUR4.js";
|
|
@@ -166,4 +166,4 @@ async function watchProject(root, opts) {
|
|
|
166
166
|
export {
|
|
167
167
|
watchProject
|
|
168
168
|
};
|
|
169
|
-
//# sourceMappingURL=chunk-
|
|
169
|
+
//# sourceMappingURL=chunk-6OSQMXUP.js.map
|
|
@@ -184,6 +184,14 @@ function stripPrefix(files, prefix) {
|
|
|
184
184
|
if (!prefix) return files;
|
|
185
185
|
return files.filter((f) => f.path.startsWith(prefix)).map((f) => ({ ...f, path: f.path.slice(prefix.length), from: f.from?.startsWith(prefix) ? f.from.slice(prefix.length) : f.from }));
|
|
186
186
|
}
|
|
187
|
+
async function changesSince(cwd, fromRef, opts = {}) {
|
|
188
|
+
if (!isSafeRef(fromRef)) return null;
|
|
189
|
+
const mb = await git(cwd, ["merge-base", fromRef, "HEAD"], { signal: opts.signal });
|
|
190
|
+
if (!mb.ok || !mb.stdout.trim()) return null;
|
|
191
|
+
const r = await git(cwd, ["diff", "--name-status", "-M", "-z", "--relative", "--no-color", mb.stdout.trim(), "HEAD", "--", "."], { signal: opts.signal });
|
|
192
|
+
if (!r.ok) return null;
|
|
193
|
+
return parseNameStatusZ(r.stdout);
|
|
194
|
+
}
|
|
187
195
|
function parseNameStatusZ(stdout) {
|
|
188
196
|
const out = [];
|
|
189
197
|
const parts = stdout.split("\0").filter(Boolean);
|
|
@@ -256,9 +264,10 @@ export {
|
|
|
256
264
|
isGitRepo,
|
|
257
265
|
gitInfo,
|
|
258
266
|
workingChanges,
|
|
267
|
+
changesSince,
|
|
259
268
|
parseNameStatusZ,
|
|
260
269
|
isSafeRef,
|
|
261
270
|
headMovement,
|
|
262
271
|
statusV2
|
|
263
272
|
};
|
|
264
|
-
//# sourceMappingURL=chunk-
|
|
273
|
+
//# sourceMappingURL=chunk-AKOI4VTD.js.map
|