@prism-d1/cli 1.0.94 ā 1.0.96
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/src/commands/bedrock-protection/scan-account.d.ts +45 -0
- package/dist/src/commands/bedrock-protection/scan-account.d.ts.map +1 -0
- package/dist/src/commands/bedrock-protection/scan-account.js +107 -0
- package/dist/src/commands/bedrock-protection/scan-account.js.map +1 -0
- package/dist/src/commands/bedrock-protection/scan-org.d.ts +23 -0
- package/dist/src/commands/bedrock-protection/scan-org.d.ts.map +1 -0
- package/dist/src/commands/bedrock-protection/scan-org.js +328 -0
- package/dist/src/commands/bedrock-protection/scan-org.js.map +1 -0
- package/dist/src/commands/bedrock-protection/scan-repo.d.ts +16 -0
- package/dist/src/commands/bedrock-protection/scan-repo.d.ts.map +1 -0
- package/dist/src/commands/bedrock-protection/scan-repo.js +397 -0
- package/dist/src/commands/bedrock-protection/scan-repo.js.map +1 -0
- package/dist/src/utils/audit-bedrock.d.ts +25 -0
- package/dist/src/utils/audit-bedrock.d.ts.map +1 -0
- package/dist/src/utils/audit-bedrock.js +331 -0
- package/dist/src/utils/audit-bedrock.js.map +1 -0
- package/dist/src/utils/audit-iam.d.ts +7 -0
- package/dist/src/utils/audit-iam.d.ts.map +1 -0
- package/dist/src/utils/audit-iam.js +239 -0
- package/dist/src/utils/audit-iam.js.map +1 -0
- package/dist/src/utils/audit-org.d.ts +80 -0
- package/dist/src/utils/audit-org.d.ts.map +1 -0
- package/dist/src/utils/audit-org.js +304 -0
- package/dist/src/utils/audit-org.js.map +1 -0
- package/dist/src/utils/audit.d.ts +61 -0
- package/dist/src/utils/audit.d.ts.map +1 -0
- package/dist/src/utils/audit.js +90 -0
- package/dist/src/utils/audit.js.map +1 -0
- package/dist/src/utils/aws.d.ts +69 -0
- package/dist/src/utils/aws.d.ts.map +1 -0
- package/dist/src/utils/aws.js +103 -0
- package/dist/src/utils/aws.js.map +1 -0
- package/dist/src/utils/exec.d.ts +12 -1
- package/dist/src/utils/exec.d.ts.map +1 -1
- package/dist/src/utils/exec.js +23 -2
- package/dist/src/utils/exec.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, resolve as resolvePath } from 'node:path';
|
|
3
|
+
import { run } from '../../utils/exec.js';
|
|
4
|
+
import { withPrivateTemp } from '../../utils/tempfile.js';
|
|
5
|
+
import { renderReport, parseFailOn, applyFailOn, FAIL_ON_OPTION, JSON_OPTION, } from '../../utils/audit.js';
|
|
6
|
+
/**
|
|
7
|
+
* Audit a git repository for credentials, using gitleaks for all detection.
|
|
8
|
+
*
|
|
9
|
+
* The account-side companion is `bedrock-protection scan-account`, which audits
|
|
10
|
+
* IAM credential hygiene and Bedrock spend guardrails. This command answers a
|
|
11
|
+
* different question: is a credential already sitting in a repository?
|
|
12
|
+
*
|
|
13
|
+
* Detection is gitleaks-only by design: this command contains no credential
|
|
14
|
+
* patterns of its own. The filename and .gitignore checks below inspect
|
|
15
|
+
* *hygiene* (is `.env` ignored? is a scanner wired into the hook?) and never
|
|
16
|
+
* attempt to recognise a secret.
|
|
17
|
+
*
|
|
18
|
+
* Three ARCC requirements shape this (SAX-03 Outcome 4, "Policy Engine Risk
|
|
19
|
+
* Detection for Credential Violations", plus the metadata-leak recommendation):
|
|
20
|
+
*
|
|
21
|
+
* - "Not scanning Git history for previously committed credentials" is a named
|
|
22
|
+
* pitfall, so history is scanned in full rather than a PR range. This is the
|
|
23
|
+
* opposite trade-off from the CI eval gate, which scans only BASE..HEAD so
|
|
24
|
+
* the gate can go green on a repo with pre-existing findings.
|
|
25
|
+
* - "Check the git history of every branch of your repo" -- satisfied by
|
|
26
|
+
* gitleaks' default, verified rather than assumed: a `gitleaks git` scan
|
|
27
|
+
* reported a finding in a commit NOT reachable from HEAD, and adding
|
|
28
|
+
* `--log-opts=--all` changed nothing.
|
|
29
|
+
* - "Do not put sensitive data into any metadata such as git commit messages."
|
|
30
|
+
* gitleaks does NOT scan commit messages -- measured: a commit whose message
|
|
31
|
+
* contained a valid AWS key id produced zero findings from both a default and
|
|
32
|
+
* an --all scan. That gap is closed by `auditCommitMessages` below, still
|
|
33
|
+
* using gitleaks for the matching.
|
|
34
|
+
*
|
|
35
|
+
* Also measured: `gitleaks dir` does NOT honour .gitignore, which is what makes
|
|
36
|
+
* the working-tree scan worth running separately -- an ignored `.env` is absent
|
|
37
|
+
* from history but present on disk.
|
|
38
|
+
*
|
|
39
|
+
* Audit-only. Nothing is written to the repository.
|
|
40
|
+
*/
|
|
41
|
+
/** Filenames that should never be tracked. A name check, not secret detection. */
|
|
42
|
+
const CREDENTIAL_FILENAMES = [
|
|
43
|
+
'.env', '.env.local', '.env.production', '.env.development',
|
|
44
|
+
'credentials', '.npmrc', '.pypirc', '.netrc',
|
|
45
|
+
'id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519',
|
|
46
|
+
];
|
|
47
|
+
/** Patterns a .gitignore should carry to keep the above from being added. */
|
|
48
|
+
const EXPECTED_IGNORES = ['.env', '*.pem', '*.key', 'credentials', 'id_rsa'];
|
|
49
|
+
function findGitleaks(explicit) {
|
|
50
|
+
if (explicit)
|
|
51
|
+
return existsSync(explicit) ? explicit : null;
|
|
52
|
+
if (process.env.GITLEAKS_PATH && existsSync(process.env.GITLEAKS_PATH))
|
|
53
|
+
return process.env.GITLEAKS_PATH;
|
|
54
|
+
const which = run('which', ['gitleaks']);
|
|
55
|
+
if (which.ok && which.stdout)
|
|
56
|
+
return which.stdout.trim();
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Run gitleaks and summarise its report.
|
|
61
|
+
*
|
|
62
|
+
* `--redact` is mandatory, not stylistic: without it gitleaks prints the matched
|
|
63
|
+
* secret into its own report and stdout, so a tool run to find an exposure would
|
|
64
|
+
* become a second copy of it -- on a terminal, in scrollback, and in whatever
|
|
65
|
+
* captures this command's output.
|
|
66
|
+
*
|
|
67
|
+
* The clean/leaks/failed distinction keys on the REPORT rather than the exit
|
|
68
|
+
* code. gitleaks exits 1 both for "leaks found" and, combined with other
|
|
69
|
+
* failures, for conditions that establish nothing -- and `run()` collapses every
|
|
70
|
+
* non-zero exit into ok:false. The report is the actual evidence: findings
|
|
71
|
+
* present means findings, while a missing or unparseable report alongside a
|
|
72
|
+
* non-zero exit means the scan produced no verdict, which is INDETERMINATE and
|
|
73
|
+
* must never read as clean.
|
|
74
|
+
*/
|
|
75
|
+
function runGitleaks(bin, args, reportPath) {
|
|
76
|
+
const res = run(bin, [...args, '--no-banner', '--redact', '--report-format=json', `--report-path=${reportPath}`]);
|
|
77
|
+
let parsed = null;
|
|
78
|
+
if (existsSync(reportPath)) {
|
|
79
|
+
try {
|
|
80
|
+
const body = readFileSync(reportPath, 'utf8').trim();
|
|
81
|
+
parsed = body ? JSON.parse(body) : [];
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
parsed = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (parsed === null) {
|
|
88
|
+
// No usable report. Exit 0 with no report is a clean scan on some gitleaks
|
|
89
|
+
// paths; a non-zero exit with no report is a failure.
|
|
90
|
+
if (res.ok)
|
|
91
|
+
return { count: 0, hits: [] };
|
|
92
|
+
return { count: -1, hits: [], error: (res.stderr || 'gitleaks failed and wrote no report').split('\n')[0] };
|
|
93
|
+
}
|
|
94
|
+
const hits = parsed.map((f) => {
|
|
95
|
+
const where = f.File ? `${f.File}:${f.StartLine}` : `line ${f.StartLine}`;
|
|
96
|
+
const commit = f.Commit ? ` @${String(f.Commit).slice(0, 8)}` : '';
|
|
97
|
+
return `[${f.RuleID}] ${where}${commit}`;
|
|
98
|
+
});
|
|
99
|
+
return { count: parsed.length, hits };
|
|
100
|
+
}
|
|
101
|
+
const ROTATE_FIRST = 'Rotate the credential FIRST. ARCC names "assuming removal from source code is sufficient" as a pitfall ā rewriting history does not un-expose anything already cloned, forked or mirrored.';
|
|
102
|
+
/**
|
|
103
|
+
* Extra args aligning this command with the CI eval gate's tuning surface.
|
|
104
|
+
*
|
|
105
|
+
* `.gitleaks.toml` needs no flag: gitleaks auto-loads it from the SCANNED path,
|
|
106
|
+
* verified rather than assumed -- a scan run from an unrelated cwd still
|
|
107
|
+
* honoured a config sitting in the target repo, in both `dir` and `git` mode.
|
|
108
|
+
* A baseline does need an explicit flag.
|
|
109
|
+
*
|
|
110
|
+
* Both are surfaced in the report, because a silently applied allowlist is
|
|
111
|
+
* indistinguishable from a clean repo, and "0 findings" means something quite
|
|
112
|
+
* different when a suppression file is in play.
|
|
113
|
+
*/
|
|
114
|
+
function tuningArgs(repo) {
|
|
115
|
+
const args = [];
|
|
116
|
+
const notes = [];
|
|
117
|
+
if (existsSync(join(repo, '.gitleaks.toml')))
|
|
118
|
+
notes.push('.gitleaks.toml (auto-loaded)');
|
|
119
|
+
const baseline = join(repo, '.prism', 'gitleaks-baseline.json');
|
|
120
|
+
if (existsSync(baseline)) {
|
|
121
|
+
args.push(`--baseline-path=${baseline}`);
|
|
122
|
+
notes.push('.prism/gitleaks-baseline.json');
|
|
123
|
+
}
|
|
124
|
+
return { args, notes };
|
|
125
|
+
}
|
|
126
|
+
function auditHistory(bin, repo, reportDir, tuning) {
|
|
127
|
+
// gitleaks scans every ref by default (verified), so this covers commits on
|
|
128
|
+
// branches other than the checked-out one.
|
|
129
|
+
const r = runGitleaks(bin, ['git', repo, ...tuning], join(reportDir, 'history.json'));
|
|
130
|
+
if (r.count < 0) {
|
|
131
|
+
return {
|
|
132
|
+
id: 'history-secrets', category: 'detection', title: 'Credentials in git history (all refs)',
|
|
133
|
+
status: 'INDETERMINATE', severity: 'CRITICAL',
|
|
134
|
+
detail: `The history scan produced no verdict (${r.error ?? 'unknown error'}), so this repository is NOT cleared.`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
id: 'history-secrets', category: 'detection', title: 'Credentials in git history (all refs)',
|
|
139
|
+
status: r.count ? 'FAIL' : 'PASS',
|
|
140
|
+
severity: 'CRITICAL',
|
|
141
|
+
detail: r.count
|
|
142
|
+
? `${r.count} credential(s) committed at some point in this repository's history: ${r.hits.slice(0, 10).join(', ')}${r.count > 10 ? `, ⦠(+${r.count - 10})` : ''}. A deleted file does not remove the blob ā it stays reachable in history.`
|
|
143
|
+
: 'No credentials found in any commit on any ref.',
|
|
144
|
+
remediation: r.count ? ROTATE_FIRST : undefined,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function auditWorkingTree(bin, repo, reportDir, tuning) {
|
|
148
|
+
// `gitleaks dir` does NOT honour .gitignore (verified), which is the point:
|
|
149
|
+
// an ignored .env never enters history but sits on disk, and leaks through
|
|
150
|
+
// backups, screen shares, bundled archives and stray `docker build` contexts.
|
|
151
|
+
const r = runGitleaks(bin, ['dir', repo, ...tuning], join(reportDir, 'worktree.json'));
|
|
152
|
+
if (r.count < 0) {
|
|
153
|
+
return {
|
|
154
|
+
id: 'worktree-secrets', category: 'detection', title: 'Credentials in the working tree (including ignored files)',
|
|
155
|
+
status: 'INDETERMINATE', severity: 'HIGH',
|
|
156
|
+
detail: `The working-tree scan produced no verdict (${r.error ?? 'unknown error'}).`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
id: 'worktree-secrets', category: 'detection', title: 'Credentials in the working tree (including ignored files)',
|
|
161
|
+
status: r.count ? 'FAIL' : 'PASS',
|
|
162
|
+
severity: 'HIGH',
|
|
163
|
+
detail: r.count
|
|
164
|
+
? `${r.count} credential(s) present on disk: ${r.hits.slice(0, 10).join(', ')}${r.count > 10 ? `, ⦠(+${r.count - 10})` : ''}. This scan ignores .gitignore, so some of these may never have been committed ā still exposed via backups, archives and build contexts.`
|
|
165
|
+
: 'No credentials on disk, including files excluded by .gitignore.',
|
|
166
|
+
remediation: r.count ? 'Move each value into Secrets Manager or Parameter Store and read it at runtime. If any is also in history, rotate first.' : undefined,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Commit messages, which gitleaks does not scan.
|
|
171
|
+
*
|
|
172
|
+
* Measured: a commit whose message contained a valid AWS key id produced zero
|
|
173
|
+
* findings from both a default `gitleaks git` scan and an `--log-opts=--all`
|
|
174
|
+
* one. ARCC explicitly calls this out ("Do not put sensitive data into any
|
|
175
|
+
* metadata such as git commit messages ... This type of data is difficult to
|
|
176
|
+
* wipe ... and are going to stay forever in the repository").
|
|
177
|
+
*
|
|
178
|
+
* Detection stays with gitleaks: the messages are written to a private temp file
|
|
179
|
+
* and gitleaks scans that. No pattern matching happens here. The temp file holds
|
|
180
|
+
* real credentials, so it goes through `withPrivateTemp` -- mode 0600 inside a
|
|
181
|
+
* mkdtemp directory, removed on every path including throws.
|
|
182
|
+
*/
|
|
183
|
+
function auditCommitMessages(bin, repo) {
|
|
184
|
+
const log = run('git', ['-C', repo, 'log', '--all', '--format=%H%n%B%n']);
|
|
185
|
+
if (!log.ok) {
|
|
186
|
+
return {
|
|
187
|
+
id: 'commit-message-secrets', category: 'detection', title: 'Credentials in commit messages',
|
|
188
|
+
status: 'INDETERMINATE', severity: 'HIGH',
|
|
189
|
+
detail: `Could not read the commit log (${(log.stderr || '').split('\n')[0] || 'unknown error'}).`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const body = log.stdout || '';
|
|
193
|
+
const lines = body.split('\n');
|
|
194
|
+
return withPrivateTemp('commit-messages.txt', body, (file) => {
|
|
195
|
+
const r = runGitleaks(bin, ['dir', file], join(dirname(file), 'messages.json'));
|
|
196
|
+
if (r.count < 0) {
|
|
197
|
+
return {
|
|
198
|
+
id: 'commit-message-secrets', category: 'detection', title: 'Credentials in commit messages',
|
|
199
|
+
status: 'INDETERMINATE', severity: 'HIGH',
|
|
200
|
+
detail: `The commit-message scan produced no verdict (${r.error ?? 'unknown error'}).`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (r.count === 0) {
|
|
204
|
+
return {
|
|
205
|
+
id: 'commit-message-secrets', category: 'detection', title: 'Credentials in commit messages',
|
|
206
|
+
status: 'PASS', severity: 'HIGH',
|
|
207
|
+
detail: 'No credentials in any commit message. Worth checking separately because gitleaks scans patch content, not message text.',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// Map each hit back to a commit: walk backwards from the reported line to
|
|
211
|
+
// the nearest 40-hex SHA, which is the %H written before each message.
|
|
212
|
+
const commits = new Set();
|
|
213
|
+
for (const hit of r.hits) {
|
|
214
|
+
const m = /line (\d+)/.exec(hit) ?? /:(\d+)/.exec(hit);
|
|
215
|
+
if (!m)
|
|
216
|
+
continue;
|
|
217
|
+
const lineNo = Number.parseInt(m[1], 10);
|
|
218
|
+
for (let i = Math.min(lineNo, lines.length) - 1; i >= 0; i--) {
|
|
219
|
+
if (/^[0-9a-f]{40}$/.test(lines[i].trim())) {
|
|
220
|
+
commits.add(lines[i].trim().slice(0, 8));
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
id: 'commit-message-secrets', category: 'detection', title: 'Credentials in commit messages',
|
|
227
|
+
status: 'FAIL', severity: 'HIGH',
|
|
228
|
+
detail: `${r.count} credential(s) in commit message text${commits.size ? `, in commit(s): ${[...commits].join(', ')}` : ''}. Message text is not part of any diff, so neither this repo's CI gate nor a plain gitleaks run would report it ā and a message cannot be edited without rewriting every descendant commit.`,
|
|
229
|
+
remediation: ROTATE_FIRST,
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
function auditTrackedFiles(repo) {
|
|
234
|
+
const ls = run('git', ['-C', repo, 'ls-files']);
|
|
235
|
+
if (!ls.ok) {
|
|
236
|
+
return {
|
|
237
|
+
id: 'tracked-credential-files', category: 'hygiene', title: 'Credential-bearing files tracked in git',
|
|
238
|
+
status: 'INDETERMINATE', severity: 'HIGH',
|
|
239
|
+
detail: `Could not list tracked files (${(ls.stderr || '').split('\n')[0] || 'unknown error'}).`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const tracked = (ls.stdout || '').split('\n').filter(Boolean);
|
|
243
|
+
const bad = tracked.filter((f) => {
|
|
244
|
+
const base = f.split('/').pop() ?? f;
|
|
245
|
+
return CREDENTIAL_FILENAMES.includes(base) || base.endsWith('.pem');
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
id: 'tracked-credential-files', category: 'hygiene', title: 'Credential-bearing files tracked in git',
|
|
249
|
+
status: bad.length ? 'FAIL' : 'PASS', severity: 'HIGH',
|
|
250
|
+
detail: bad.length
|
|
251
|
+
? `${bad.length} file(s) of a kind that normally holds credentials are tracked: ${bad.slice(0, 10).join(', ')}. Flagged by filename ā whether each actually contains a live credential is answered by the history and working-tree scans above.`
|
|
252
|
+
: `None of ${tracked.length} tracked file(s) has a credential-bearing filename.`,
|
|
253
|
+
remediation: bad.length ? 'Untrack each file, add it to .gitignore, and rotate anything it contained ā it is in history regardless.' : undefined,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function auditGitignore(repo) {
|
|
257
|
+
const path = join(repo, '.gitignore');
|
|
258
|
+
if (!existsSync(path)) {
|
|
259
|
+
return {
|
|
260
|
+
id: 'gitignore-hygiene', category: 'hygiene', title: '.gitignore covers credential file patterns',
|
|
261
|
+
status: 'FAIL', severity: 'MEDIUM',
|
|
262
|
+
detail: 'No .gitignore at the repository root, so nothing stops a .env or private key from being staged by an ordinary `git add -A`.',
|
|
263
|
+
remediation: `Add a .gitignore covering at least: ${EXPECTED_IGNORES.join(', ')}.`,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const content = readFileSync(path, 'utf8');
|
|
267
|
+
const lines = content.split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#'));
|
|
268
|
+
const missing = EXPECTED_IGNORES.filter((pat) => !lines.some((l) => l === pat || l === `/${pat}` || l === `**/${pat}`));
|
|
269
|
+
return {
|
|
270
|
+
id: 'gitignore-hygiene', category: 'hygiene', title: '.gitignore covers credential file patterns',
|
|
271
|
+
status: missing.length ? 'FAIL' : 'PASS', severity: 'MEDIUM',
|
|
272
|
+
detail: missing.length
|
|
273
|
+
? `.gitignore does not cover: ${missing.join(', ')}. Each is a pattern a credential commonly arrives under, and an uncovered one gets staged by a routine \`git add -A\`.`
|
|
274
|
+
: 'All expected credential file patterns are ignored.',
|
|
275
|
+
remediation: missing.length ? `Add the missing patterns: ${missing.join(', ')}.` : undefined,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function auditPreCommitHook(repo) {
|
|
279
|
+
// Look in both the default hooks dir and core.hooksPath, since a repo using
|
|
280
|
+
// pre-commit or husky relocates it and .git/hooks would look empty.
|
|
281
|
+
const candidates = [];
|
|
282
|
+
const hooksPath = run('git', ['-C', repo, 'config', '--get', 'core.hooksPath']);
|
|
283
|
+
if (hooksPath.ok && hooksPath.stdout.trim())
|
|
284
|
+
candidates.push(resolvePath(repo, hooksPath.stdout.trim()));
|
|
285
|
+
candidates.push(join(repo, '.git', 'hooks'));
|
|
286
|
+
let wired = false;
|
|
287
|
+
const inspected = [];
|
|
288
|
+
for (const dir of candidates) {
|
|
289
|
+
if (!existsSync(dir))
|
|
290
|
+
continue;
|
|
291
|
+
inspected.push(dir);
|
|
292
|
+
for (const entry of readdirSync(dir)) {
|
|
293
|
+
if (!entry.startsWith('pre-commit'))
|
|
294
|
+
continue;
|
|
295
|
+
try {
|
|
296
|
+
if (/gitleaks/i.test(readFileSync(join(dir, entry), 'utf8'))) {
|
|
297
|
+
wired = true;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch { /* unreadable hook ā treat as not wired */ }
|
|
302
|
+
}
|
|
303
|
+
if (wired)
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
// A pre-commit config that references gitleaks also counts.
|
|
307
|
+
const preCommitCfg = join(repo, '.pre-commit-config.yaml');
|
|
308
|
+
if (!wired && existsSync(preCommitCfg)) {
|
|
309
|
+
try {
|
|
310
|
+
wired = /gitleaks/i.test(readFileSync(preCommitCfg, 'utf8'));
|
|
311
|
+
}
|
|
312
|
+
catch { /* ignore */ }
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
id: 'pre-commit-scanner', category: 'hygiene', title: 'gitleaks wired as a pre-commit hook',
|
|
316
|
+
status: wired ? 'PASS' : 'FAIL', severity: 'LOW',
|
|
317
|
+
detail: wired
|
|
318
|
+
? 'A pre-commit hook or .pre-commit-config.yaml invokes gitleaks, so a credential is caught before it becomes a commit.'
|
|
319
|
+
: `No pre-commit hook invokes gitleaks${inspected.length ? ` (checked ${inspected.join(', ')})` : ''}. Without one, the earliest catch is CI ā by which point the credential is already in history and, on a push, already off the machine.`,
|
|
320
|
+
remediation: wired ? undefined : 'Install `gitleaks protect --staged --redact` as a pre-commit hook. Severity is LOW only because a hook is advisory: it is trivially bypassed with --no-verify and is not a control.',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
export default {
|
|
324
|
+
description: 'Scan a git repository for credentials with gitleaks: full history across all refs, working tree, commit messages, and ignore-file hygiene',
|
|
325
|
+
options: [
|
|
326
|
+
{ flags: '--repo <path>', description: 'Repository to scan', default: '.' },
|
|
327
|
+
{ flags: '--gitleaks <path>', description: 'Path to the gitleaks binary (default: $GITLEAKS_PATH, then PATH)' },
|
|
328
|
+
{ flags: '--skip-history', description: 'Skip the full-history scan (it is the slow one on a large repo)' },
|
|
329
|
+
JSON_OPTION,
|
|
330
|
+
FAIL_ON_OPTION,
|
|
331
|
+
],
|
|
332
|
+
async action(opts) {
|
|
333
|
+
const failOn = parseFailOn(opts.failOn);
|
|
334
|
+
const repo = resolvePath(opts.repo || '.');
|
|
335
|
+
if (!existsSync(join(repo, '.git'))) {
|
|
336
|
+
console.error(`\nā ${repo} is not a git repository (no .git directory).\n`);
|
|
337
|
+
process.exitCode = 2;
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
// Deliberately not auto-downloaded. The CI eval gate fetches a
|
|
341
|
+
// version-pinned, checksum-verified binary because it runs in a disposable
|
|
342
|
+
// container; a developer CLI silently pulling an executable onto a
|
|
343
|
+
// workstation is a different risk, so this reports and stops.
|
|
344
|
+
const bin = findGitleaks(opts.gitleaks);
|
|
345
|
+
const tuning = tuningArgs(repo);
|
|
346
|
+
const findings = [];
|
|
347
|
+
if (!bin) {
|
|
348
|
+
for (const [id, title, sev] of [
|
|
349
|
+
['history-secrets', 'Credentials in git history (all refs)', 'CRITICAL'],
|
|
350
|
+
['worktree-secrets', 'Credentials in the working tree (including ignored files)', 'HIGH'],
|
|
351
|
+
['commit-message-secrets', 'Credentials in commit messages', 'HIGH'],
|
|
352
|
+
]) {
|
|
353
|
+
findings.push({
|
|
354
|
+
id, category: 'detection', title,
|
|
355
|
+
status: 'INDETERMINATE', severity: sev,
|
|
356
|
+
detail: 'gitleaks is not installed, so no credential detection ran. This repository is NOT cleared.',
|
|
357
|
+
remediation: 'Install gitleaks (`brew install gitleaks`, or a release binary from github.com/gitleaks/gitleaks), or pass --gitleaks <path>.',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
// One temp dir holds every gitleaks report; reports can name files but
|
|
363
|
+
// never contain secret values, because --redact is always passed.
|
|
364
|
+
withPrivateTemp('.keep', '', (keep) => {
|
|
365
|
+
const reportDir = dirname(keep);
|
|
366
|
+
if (opts.skipHistory) {
|
|
367
|
+
findings.push({
|
|
368
|
+
id: 'history-secrets', category: 'detection', title: 'Credentials in git history (all refs)',
|
|
369
|
+
status: 'INDETERMINATE', severity: 'CRITICAL',
|
|
370
|
+
detail: 'Skipped via --skip-history. The check most likely to find an already-leaked credential did not run.',
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
findings.push(auditHistory(bin, repo, reportDir, tuning.args));
|
|
375
|
+
}
|
|
376
|
+
findings.push(auditWorkingTree(bin, repo, reportDir, tuning.args));
|
|
377
|
+
findings.push(auditCommitMessages(bin, repo));
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
findings.push(auditTrackedFiles(repo));
|
|
381
|
+
findings.push(auditGitignore(repo));
|
|
382
|
+
findings.push(auditPreCommitHook(repo));
|
|
383
|
+
renderReport(findings, {
|
|
384
|
+
title: 'š Repository credential scan',
|
|
385
|
+
subtitle: `Repo: ${repo}${bin ? ` gitleaks: ${bin}` : ' gitleaks: NOT FOUND'}`
|
|
386
|
+
+ (tuning.notes.length ? `\n Suppression in effect: ${tuning.notes.join(', ')} ā "0 findings" is relative to these.` : ''),
|
|
387
|
+
groups: [
|
|
388
|
+
['detection', 'Credential detection (gitleaks)'],
|
|
389
|
+
['hygiene', 'Repository hygiene ā what stops the next one'],
|
|
390
|
+
],
|
|
391
|
+
json: Boolean(opts.json),
|
|
392
|
+
jsonContext: { repo, gitleaks: bin, suppression: tuning.notes },
|
|
393
|
+
});
|
|
394
|
+
applyFailOn(findings, failOn, Boolean(opts.json));
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
//# sourceMappingURL=scan-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-repo.js","sourceRoot":"","sources":["../../../../src/commands/bedrock-protection/scan-repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AwsTarget } from './aws.js';
|
|
2
|
+
import { Finding } from './audit.js';
|
|
3
|
+
export declare function auditBudgets(target: AwsTarget | undefined, accountId: string): {
|
|
4
|
+
findings: Finding[];
|
|
5
|
+
coveringBudgets: string[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* A budget that alerts nobody is a record, not a control.
|
|
9
|
+
*
|
|
10
|
+
* `describe-budgets` says nothing about whether anyone is told when a threshold
|
|
11
|
+
* is crossed -- notifications and their subscribers are separate resources. A
|
|
12
|
+
* budget created without them still shows a spend bar in the console and still
|
|
13
|
+
* reports as "a Bedrock budget exists", while nothing reaches a human. For
|
|
14
|
+
* LLMjacking specifically, being told is the entire value: the spend has already
|
|
15
|
+
* happened by the time anyone opens the Billing console.
|
|
16
|
+
*
|
|
17
|
+
* `DescribeBudgetNotificationsForAccount` returns notifications for every budget
|
|
18
|
+
* in ONE call. Subscribers need a call per notification, which is why this is
|
|
19
|
+
* scoped to the budgets that actually cover Bedrock rather than all of them.
|
|
20
|
+
*/
|
|
21
|
+
export declare function auditBudgetAlerting(target: AwsTarget | undefined, accountId: string, coveringBudgets: string[]): Finding;
|
|
22
|
+
export declare function auditDetection(target: AwsTarget | undefined, region: string): Finding[];
|
|
23
|
+
export declare function auditCommitments(target: AwsTarget | undefined, region: string): Finding;
|
|
24
|
+
export declare function auditForensics(target: AwsTarget | undefined, region: string): Finding;
|
|
25
|
+
//# sourceMappingURL=audit-bedrock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-bedrock.d.ts","sourceRoot":"","sources":["../../../src/utils/audit-bedrock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiErC,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,CA2DjI;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CA8DT;AAWD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,CA+FvF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAmBvF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAoBrF"}
|