@promptci/core 0.0.1 → 0.1.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/LICENSE +201 -201
- package/README.md +29 -30
- package/dist/agent-practices.d.ts +2 -0
- package/dist/agent-practices.d.ts.map +1 -1
- package/dist/agent-practices.js +91 -51
- package/dist/agent-practices.js.map +1 -1
- package/dist/ai-config.d.ts +139 -0
- package/dist/ai-config.d.ts.map +1 -0
- package/dist/ai-config.js +523 -0
- package/dist/ai-config.js.map +1 -0
- package/dist/baseline.d.ts +17 -1
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +5 -1
- package/dist/baseline.js.map +1 -1
- package/dist/buried-critical.d.ts +17 -0
- package/dist/buried-critical.d.ts.map +1 -0
- package/dist/buried-critical.js +112 -0
- package/dist/buried-critical.js.map +1 -0
- package/dist/canonical-owner.d.ts.map +1 -1
- package/dist/canonical-owner.js +18 -8
- package/dist/canonical-owner.js.map +1 -1
- package/dist/command-validity.d.ts.map +1 -1
- package/dist/command-validity.js +53 -74
- package/dist/command-validity.js.map +1 -1
- package/dist/conflicts.d.ts.map +1 -1
- package/dist/conflicts.js +1 -42
- package/dist/conflicts.js.map +1 -1
- package/dist/context-analysis.d.ts.map +1 -1
- package/dist/context-analysis.js +13 -2
- package/dist/context-analysis.js.map +1 -1
- package/dist/context-bloat.d.ts +19 -0
- package/dist/context-bloat.d.ts.map +1 -1
- package/dist/context-bloat.js +82 -9
- package/dist/context-bloat.js.map +1 -1
- package/dist/context-optimizer.d.ts.map +1 -1
- package/dist/context-optimizer.js +15 -54
- package/dist/context-optimizer.js.map +1 -1
- package/dist/copilot-instructions-detector.d.ts +24 -0
- package/dist/copilot-instructions-detector.d.ts.map +1 -0
- package/dist/copilot-instructions-detector.js +145 -0
- package/dist/copilot-instructions-detector.js.map +1 -0
- package/dist/cursor-rules-detector.d.ts +17 -0
- package/dist/cursor-rules-detector.d.ts.map +1 -0
- package/dist/cursor-rules-detector.js +110 -0
- package/dist/cursor-rules-detector.js.map +1 -0
- package/dist/custom-rules.d.ts +52 -0
- package/dist/custom-rules.d.ts.map +1 -0
- package/dist/custom-rules.js +298 -0
- package/dist/custom-rules.js.map +1 -0
- package/dist/dead-references.js +11 -9
- package/dist/dead-references.js.map +1 -1
- package/dist/detectors.d.ts +1 -3
- package/dist/detectors.d.ts.map +1 -1
- package/dist/detectors.js +46 -53
- package/dist/detectors.js.map +1 -1
- package/dist/duplicates.d.ts.map +1 -1
- package/dist/duplicates.js +10 -5
- package/dist/duplicates.js.map +1 -1
- package/dist/evidence.d.ts +28 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +38 -0
- package/dist/evidence.js.map +1 -0
- package/dist/fix-engine.d.ts +11 -0
- package/dist/fix-engine.d.ts.map +1 -1
- package/dist/fix-engine.js +88 -71
- package/dist/fix-engine.js.map +1 -1
- package/dist/frameworks/python.d.ts.map +1 -1
- package/dist/frameworks/python.js +5 -2
- package/dist/frameworks/python.js.map +1 -1
- package/dist/health-score.d.ts.map +1 -1
- package/dist/health-score.js +1 -0
- package/dist/health-score.js.map +1 -1
- package/dist/hooks-settings-detector.d.ts +17 -0
- package/dist/hooks-settings-detector.d.ts.map +1 -0
- package/dist/hooks-settings-detector.js +228 -0
- package/dist/hooks-settings-detector.js.map +1 -0
- package/dist/index.d.ts +22 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/manifest-consistency.d.ts +6 -0
- package/dist/manifest-consistency.d.ts.map +1 -1
- package/dist/manifest-consistency.js +26 -5
- package/dist/manifest-consistency.js.map +1 -1
- package/dist/markdown-fences.d.ts +112 -0
- package/dist/markdown-fences.d.ts.map +1 -0
- package/dist/markdown-fences.js +174 -0
- package/dist/markdown-fences.js.map +1 -0
- package/dist/mcp-config-detector.d.ts +20 -0
- package/dist/mcp-config-detector.d.ts.map +1 -0
- package/dist/mcp-config-detector.js +237 -0
- package/dist/mcp-config-detector.js.map +1 -0
- package/dist/missing-context.d.ts.map +1 -1
- package/dist/missing-context.js +2 -52
- package/dist/missing-context.js.map +1 -1
- package/dist/model-budgets.d.ts +69 -0
- package/dist/model-budgets.d.ts.map +1 -0
- package/dist/model-budgets.js +110 -0
- package/dist/model-budgets.js.map +1 -0
- package/dist/negative-instructions.d.ts +26 -0
- package/dist/negative-instructions.d.ts.map +1 -0
- package/dist/negative-instructions.js +174 -0
- package/dist/negative-instructions.js.map +1 -0
- package/dist/path-containment.d.ts +12 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +53 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/project-type.d.ts +12 -2
- package/dist/project-type.d.ts.map +1 -1
- package/dist/project-type.js +25 -12
- package/dist/project-type.js.map +1 -1
- package/dist/prompt-cache.d.ts.map +1 -1
- package/dist/prompt-cache.js +26 -19
- package/dist/prompt-cache.js.map +1 -1
- package/dist/promptci-gitignore.d.ts +34 -0
- package/dist/promptci-gitignore.d.ts.map +1 -0
- package/dist/promptci-gitignore.js +42 -0
- package/dist/promptci-gitignore.js.map +1 -0
- package/dist/repo-context.d.ts +27 -0
- package/dist/repo-context.d.ts.map +1 -1
- package/dist/repo-context.js +42 -11
- package/dist/repo-context.js.map +1 -1
- package/dist/report.d.ts +1 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +132 -61
- package/dist/report.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +7 -3
- package/dist/scan.js.map +1 -1
- package/dist/scanner.d.ts +13 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +49 -37
- package/dist/scanner.js.map +1 -1
- package/dist/security-pack.d.ts +20 -0
- package/dist/security-pack.d.ts.map +1 -1
- package/dist/security-pack.js +27 -10
- package/dist/security-pack.js.map +1 -1
- package/dist/skills-detector.d.ts +19 -0
- package/dist/skills-detector.d.ts.map +1 -0
- package/dist/skills-detector.js +203 -0
- package/dist/skills-detector.js.map +1 -0
- package/dist/stale-instructions.d.ts +4 -1
- package/dist/stale-instructions.d.ts.map +1 -1
- package/dist/stale-instructions.js +51 -54
- package/dist/stale-instructions.js.map +1 -1
- package/dist/subagents-detector.d.ts +19 -0
- package/dist/subagents-detector.d.ts.map +1 -0
- package/dist/subagents-detector.js +155 -0
- package/dist/subagents-detector.js.map +1 -0
- package/dist/suppression.d.ts.map +1 -1
- package/dist/suppression.js +11 -45
- package/dist/suppression.js.map +1 -1
- package/dist/types.d.ts +28 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +25 -1
- package/dist/types.js.map +1 -1
- package/dist/vague-guidance.d.ts +15 -2
- package/dist/vague-guidance.d.ts.map +1 -1
- package/dist/vague-guidance.js +42 -40
- package/dist/vague-guidance.js.map +1 -1
- package/dist/windsurf-detector.d.ts +22 -0
- package/dist/windsurf-detector.d.ts.map +1 -0
- package/dist/windsurf-detector.js +87 -0
- package/dist/windsurf-detector.js.map +1 -0
- package/dist/within-section-dedup.d.ts +16 -0
- package/dist/within-section-dedup.d.ts.map +1 -0
- package/dist/within-section-dedup.js +153 -0
- package/dist/within-section-dedup.js.map +1 -0
- package/package.json +11 -8
- package/dist/cheap-model-routing.d.ts +0 -4
- package/dist/cheap-model-routing.d.ts.map +0 -1
- package/dist/cheap-model-routing.js +0 -43
- package/dist/cheap-model-routing.js.map +0 -1
- package/dist/cli-output-filter.d.ts +0 -16
- package/dist/cli-output-filter.d.ts.map +0 -1
- package/dist/cli-output-filter.js +0 -88
- package/dist/cli-output-filter.js.map +0 -1
- package/dist/context-cost.d.ts +0 -4
- package/dist/context-cost.d.ts.map +0 -1
- package/dist/context-cost.js +0 -70
- package/dist/context-cost.js.map +0 -1
- package/dist/context-recommender.d.ts +0 -3
- package/dist/context-recommender.d.ts.map +0 -1
- package/dist/context-recommender.js +0 -329
- package/dist/context-recommender.js.map +0 -1
- package/dist/llm.d.ts +0 -6
- package/dist/llm.d.ts.map +0 -1
- package/dist/llm.js +0 -67
- package/dist/llm.js.map +0 -1
- package/dist/mcp-detector.d.ts +0 -4
- package/dist/mcp-detector.d.ts.map +0 -1
- package/dist/mcp-detector.js +0 -131
- package/dist/mcp-detector.js.map +0 -1
- package/dist/native-tool-preference.d.ts +0 -20
- package/dist/native-tool-preference.d.ts.map +0 -1
- package/dist/native-tool-preference.js +0 -94
- package/dist/native-tool-preference.js.map +0 -1
- package/dist/output-verbosity.d.ts +0 -3
- package/dist/output-verbosity.d.ts.map +0 -1
- package/dist/output-verbosity.js +0 -83
- package/dist/output-verbosity.js.map +0 -1
- package/dist/product-boundary.d.ts +0 -11
- package/dist/product-boundary.d.ts.map +0 -1
- package/dist/product-boundary.js +0 -301
- package/dist/product-boundary.js.map +0 -1
package/dist/fix-engine.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import * as fs from 'node:fs/promises';
|
|
3
|
+
import { renderPromptCiGitignore } from './promptci-gitignore.js';
|
|
4
|
+
import { isWithinRoot } from './path-containment.js';
|
|
5
|
+
import { gitignoreLineMatchesTarget } from './security-pack.js';
|
|
6
|
+
/**
|
|
7
|
+
* True when an active (non-comment) line of the given .gitignore content
|
|
8
|
+
* already covers `target`, using the same segment-based matching as the
|
|
9
|
+
* security-pack detector. The fix side must agree with the detector: a
|
|
10
|
+
* substring test would let an entry like `mydist/` suppress adding `dist/`
|
|
11
|
+
* even though the detector (correctly) still flags `dist` as unignored.
|
|
12
|
+
*/
|
|
13
|
+
function gitignoreCovers(content, target) {
|
|
14
|
+
return content
|
|
15
|
+
.split(/\r?\n/)
|
|
16
|
+
.map((l) => l.trim())
|
|
17
|
+
.filter((l) => l && !l.startsWith('#'))
|
|
18
|
+
.some((line) => gitignoreLineMatchesTarget(line, target));
|
|
19
|
+
}
|
|
3
20
|
/**
|
|
4
21
|
* Validates and resolves a path relative to the repo root to prevent path traversal.
|
|
5
22
|
*/
|
|
@@ -8,7 +25,7 @@ export function resolveSafePath(repoRoot, relativeOrAbsolutePath) {
|
|
|
8
25
|
const resolvedPath = path.isAbsolute(relativeOrAbsolutePath)
|
|
9
26
|
? path.resolve(relativeOrAbsolutePath)
|
|
10
27
|
: path.resolve(resolvedRoot, relativeOrAbsolutePath);
|
|
11
|
-
if (!
|
|
28
|
+
if (!isWithinRoot(resolvedRoot, resolvedPath)) {
|
|
12
29
|
throw new Error(`Path traversal detected: "${relativeOrAbsolutePath}" is outside repo root "${resolvedRoot}"`);
|
|
13
30
|
}
|
|
14
31
|
return resolvedPath;
|
|
@@ -29,11 +46,14 @@ export async function applyFixRecipe(issue, repoRoot) {
|
|
|
29
46
|
catch {
|
|
30
47
|
// file doesn't exist, which is fine
|
|
31
48
|
}
|
|
32
|
-
if (!content
|
|
49
|
+
if (!gitignoreCovers(content, '.promptci')) {
|
|
33
50
|
const lineEnding = content.includes('\r\n') ? '\r\n' : '\n';
|
|
51
|
+
// BUG-7: write the baseline-preserving stanza, not a bare `.promptci/`
|
|
52
|
+
// — the latter also ignores baseline.json and breaks the CI ratchet.
|
|
53
|
+
const block = renderPromptCiGitignore(lineEnding);
|
|
34
54
|
const suffix = content.endsWith('\n') || content.endsWith('\r')
|
|
35
|
-
?
|
|
36
|
-
: `${lineEnding}
|
|
55
|
+
? block
|
|
56
|
+
: `${lineEnding}${block}`;
|
|
37
57
|
changes.push({
|
|
38
58
|
filePath: gitignorePath,
|
|
39
59
|
originalContent: content,
|
|
@@ -58,7 +78,7 @@ export async function applyFixRecipe(issue, repoRoot) {
|
|
|
58
78
|
const m = /Directory "([^"]+)" exists/.exec(ev);
|
|
59
79
|
if (m && m[1]) {
|
|
60
80
|
const dirName = m[1];
|
|
61
|
-
if (!content
|
|
81
|
+
if (!gitignoreCovers(content, dirName)) {
|
|
62
82
|
newLines += `${dirName}/${lineEnding}`;
|
|
63
83
|
}
|
|
64
84
|
}
|
|
@@ -75,45 +95,22 @@ export async function applyFixRecipe(issue, repoRoot) {
|
|
|
75
95
|
}
|
|
76
96
|
return changes;
|
|
77
97
|
}
|
|
78
|
-
// 3.
|
|
98
|
+
// 3. Stale years are intentionally NOT auto-fixable.
|
|
99
|
+
//
|
|
100
|
+
// BUG-14: this branch used to rewrite every year matching a hardcoded
|
|
101
|
+
// 2019–2023 alternation to the literal string '2026' across the whole
|
|
102
|
+
// flagged section. Three problems, any one of which is disqualifying:
|
|
103
|
+
// - the source range and the replacement year were both hardcoded, so the
|
|
104
|
+
// recipe was wrong from Jan 1 2027 onward;
|
|
105
|
+
// - the range disagreed with the detector's own (2019–2025), so the fixer
|
|
106
|
+
// silently skipped most of what the detector flagged; and
|
|
107
|
+
// - a bare year substitution cannot tell a stale instruction from a
|
|
108
|
+
// copyright line, a release date, or a version pin — it rewrote
|
|
109
|
+
// "© 2021", "shipped in 2020", and "the 2022 spec" alike.
|
|
110
|
+
//
|
|
111
|
+
// The finding now carries an advisory `fixRecipe` string instead (see
|
|
112
|
+
// stale-instructions.ts) and produces no automated edit.
|
|
79
113
|
if (issue.id.startsWith('stale-') && issue.category === 'stale_instruction') {
|
|
80
|
-
const STALE_YEAR_RE = /(?<![/-])\b(2019|2020|2021|2022|2023)\b(?![/-])/g;
|
|
81
|
-
// Check if this issue is specifically about stale years
|
|
82
|
-
const hasYearEvidence = issue.evidence.some(ev => ev.includes('Year reference(s) that may be outdated'));
|
|
83
|
-
if (hasYearEvidence && issue.locations.length > 0) {
|
|
84
|
-
const loc = issue.locations[0];
|
|
85
|
-
const filePath = resolveSafePath(repoRoot, loc.filePath);
|
|
86
|
-
let content = '';
|
|
87
|
-
try {
|
|
88
|
-
content = await fs.readFile(filePath, 'utf-8');
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
const lines = content.split(/\r?\n/);
|
|
94
|
-
const hasCRLF = content.includes('\r\n');
|
|
95
|
-
const lineEnding = hasCRLF ? '\r\n' : '\n';
|
|
96
|
-
const startIdx = (loc.startLine ?? 1) - 1;
|
|
97
|
-
const endIdx = (loc.endLine ?? lines.length) - 1;
|
|
98
|
-
let modified = false;
|
|
99
|
-
for (let i = startIdx; i <= endIdx; i++) {
|
|
100
|
-
if (i >= 0 && i < lines.length) {
|
|
101
|
-
const original = lines[i];
|
|
102
|
-
const updated = original.replace(STALE_YEAR_RE, '2026');
|
|
103
|
-
if (updated !== original) {
|
|
104
|
-
lines[i] = updated;
|
|
105
|
-
modified = true;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (modified) {
|
|
110
|
-
changes.push({
|
|
111
|
-
filePath,
|
|
112
|
-
originalContent: content,
|
|
113
|
-
newContent: lines.join(lineEnding),
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
114
|
return changes;
|
|
118
115
|
}
|
|
119
116
|
// 4. Duplicate Markdown Section Consolidation
|
|
@@ -125,12 +122,22 @@ export async function applyFixRecipe(issue, repoRoot) {
|
|
|
125
122
|
}
|
|
126
123
|
const canonicalPath = resolveSafePath(repoRoot, canonicalLoc.filePath);
|
|
127
124
|
const canonicalBase = path.basename(canonicalPath);
|
|
128
|
-
// 4b.
|
|
125
|
+
// 4b. Group the non-canonical locations by file. A single file can contain
|
|
126
|
+
// the duplicated section more than once; computing one FileChange per
|
|
127
|
+
// location from the original content and writing them in sequence would
|
|
128
|
+
// clobber all but the last, so each file is rewritten exactly once with
|
|
129
|
+
// every one of its duplicate ranges replaced (BUG: same-file collision).
|
|
130
|
+
const byFile = new Map();
|
|
129
131
|
for (const loc of issue.locations) {
|
|
130
132
|
const filePath = resolveSafePath(repoRoot, loc.filePath);
|
|
131
133
|
if (filePath === canonicalPath)
|
|
132
134
|
continue; // skip canonical file
|
|
133
|
-
|
|
135
|
+
const list = byFile.get(filePath) ?? [];
|
|
136
|
+
list.push(loc);
|
|
137
|
+
byFile.set(filePath, list);
|
|
138
|
+
}
|
|
139
|
+
for (const [filePath, locs] of byFile) {
|
|
140
|
+
let content;
|
|
134
141
|
try {
|
|
135
142
|
content = await fs.readFile(filePath, 'utf-8');
|
|
136
143
|
}
|
|
@@ -138,42 +145,52 @@ export async function applyFixRecipe(issue, repoRoot) {
|
|
|
138
145
|
continue;
|
|
139
146
|
}
|
|
140
147
|
const lines = content.split(/\r?\n/);
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
const startIdx = (loc.startLine ?? 1) - 1;
|
|
144
|
-
const endIdx = (loc.endLine ?? lines.length) - 1;
|
|
145
|
-
// Find relative path from current file to canonical file for clean Markdown links
|
|
148
|
+
const lineEnding = content.includes('\r\n') ? '\r\n' : '\n';
|
|
149
|
+
// Relative path from this file to the canonical file for a clean link.
|
|
146
150
|
const relPath = path.relative(path.dirname(filePath), canonicalPath).replace(/\\/g, '/');
|
|
147
151
|
const linkTarget = relPath.startsWith('.') ? relPath : `./${relPath}`;
|
|
148
|
-
// Extract the section lines
|
|
149
|
-
const sectionLines = lines.slice(startIdx, endIdx + 1);
|
|
150
|
-
let headingLine = '';
|
|
151
|
-
let replaceStartIdx = startIdx;
|
|
152
|
-
// Check if the section starts with a heading line (e.g. "## Deployment")
|
|
153
|
-
// If so, we preserve the heading and replace only the body below it.
|
|
154
|
-
if (sectionLines.length > 0 && sectionLines[0].trim().startsWith('#')) {
|
|
155
|
-
headingLine = sectionLines[0];
|
|
156
|
-
replaceStartIdx = startIdx + 1;
|
|
157
|
-
}
|
|
158
152
|
const pointerText = `See canonical instructions in [${canonicalBase}](${linkTarget}) for details.`;
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
153
|
+
// Replace each duplicate range bottom-up so an earlier (higher-line)
|
|
154
|
+
// replacement never shifts the indices of ranges still to be processed.
|
|
155
|
+
const ranges = locs
|
|
156
|
+
.map(loc => ({
|
|
157
|
+
startIdx: (loc.startLine ?? 1) - 1,
|
|
158
|
+
endIdx: (loc.endLine ?? lines.length) - 1,
|
|
159
|
+
}))
|
|
160
|
+
.sort((a, b) => b.startIdx - a.startIdx);
|
|
161
|
+
let working = lines;
|
|
162
|
+
for (const { startIdx, endIdx } of ranges) {
|
|
163
|
+
const sectionLines = working.slice(startIdx, endIdx + 1);
|
|
164
|
+
// Preserve a leading heading line (e.g. "## Deployment") and replace
|
|
165
|
+
// only the body below it; otherwise replace the whole range.
|
|
166
|
+
const hasHeading = sectionLines.length > 0 && sectionLines[0].trim().startsWith('#');
|
|
167
|
+
const replaceStartIdx = hasHeading ? startIdx + 1 : startIdx;
|
|
168
|
+
working = [
|
|
169
|
+
...working.slice(0, replaceStartIdx),
|
|
170
|
+
pointerText,
|
|
171
|
+
...working.slice(endIdx + 1),
|
|
172
|
+
];
|
|
168
173
|
}
|
|
169
|
-
const finalLines = [...prefixLines, ...newSectionContent, ...suffixLines];
|
|
170
174
|
changes.push({
|
|
171
175
|
filePath,
|
|
172
176
|
originalContent: content,
|
|
173
|
-
newContent:
|
|
177
|
+
newContent: working.join(lineEnding),
|
|
174
178
|
});
|
|
175
179
|
}
|
|
176
180
|
}
|
|
177
181
|
return changes;
|
|
178
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* True when `promptci fix` may mechanically apply this issue's recipe.
|
|
185
|
+
*
|
|
186
|
+
* Driven by the explicit `autoApplySafe` flag the emitting detector sets — it is
|
|
187
|
+
* deliberately NOT `Boolean(issue.fixRecipe)`. Most `fixRecipe` strings are
|
|
188
|
+
* advisory prose that needs human judgment (stale years, framework/runtime
|
|
189
|
+
* guidance, agent-practices), and auto-applying them would corrupt files. Only
|
|
190
|
+
* the handful of recipes that `applyFixRecipe` implements as a mechanical,
|
|
191
|
+
* reversible edit are marked safe at their source.
|
|
192
|
+
*/
|
|
193
|
+
export function isRepairable(issue) {
|
|
194
|
+
return issue.autoApplySafe === true;
|
|
195
|
+
}
|
|
179
196
|
//# sourceMappingURL=fix-engine.js.map
|
package/dist/fix-engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix-engine.js","sourceRoot":"","sources":["../src/fix-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"fix-engine.js","sourceRoot":"","sources":["../src/fix-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/frameworks/python.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../src/frameworks/python.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,EAAE,CAqElE"}
|
|
@@ -4,7 +4,10 @@ export function detectPython(context) {
|
|
|
4
4
|
const issues = [];
|
|
5
5
|
const { manifests, repoRoot, files } = context;
|
|
6
6
|
const hasPyProject = !!manifests.pyproject;
|
|
7
|
-
|
|
7
|
+
// BUG-004: requirements.txt content is read once, asynchronously, while the
|
|
8
|
+
// RepoContext is built — the detector must not do synchronous disk I/O inside
|
|
9
|
+
// the scan pipeline. Presence is derived from that captured content.
|
|
10
|
+
const hasRequirements = manifests.requirements !== undefined;
|
|
8
11
|
const hasPipfile = fs.existsSync(path.join(repoRoot, 'Pipfile'));
|
|
9
12
|
const hasCondaYaml = fs.existsSync(path.join(repoRoot, 'environment.yml'));
|
|
10
13
|
if (!hasPyProject && !hasRequirements && !hasPipfile && !hasCondaYaml) {
|
|
@@ -12,7 +15,7 @@ export function detectPython(context) {
|
|
|
12
15
|
}
|
|
13
16
|
// Check for missing pytest guidance if pytest is in deps
|
|
14
17
|
const isPytestInDeps = (manifests.pyproject?.includes('pytest') ||
|
|
15
|
-
|
|
18
|
+
manifests.requirements?.includes('pytest'));
|
|
16
19
|
if (isPytestInDeps) {
|
|
17
20
|
const hasPytestGuidance = files.some(file => file.content.toLowerCase().includes('pytest') ||
|
|
18
21
|
file.content.toLowerCase().includes('test guidance'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python.js","sourceRoot":"","sources":["../../src/frameworks/python.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,MAAM,UAAU,YAAY,CAAC,OAAoB;IAC/C,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE/C,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IAC3C,MAAM,eAAe,GAAG,
|
|
1
|
+
{"version":3,"file":"python.js","sourceRoot":"","sources":["../../src/frameworks/python.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health-score.d.ts","sourceRoot":"","sources":["../src/health-score.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAgC,aAAa,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"health-score.d.ts","sourceRoot":"","sources":["../src/health-score.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAgC,aAAa,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAyBtF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAoBlE;AAQD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAiE9E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,EAAE,CAuChE"}
|
package/dist/health-score.js
CHANGED
package/dist/health-score.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health-score.js","sourceRoot":"","sources":["../src/health-score.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,eAAe,GAAkC;IACrD,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,CAAC;CACR,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,sBAAsB,GAA2C;IACrE,SAAS,EAAE,EAAE,EAAW,kBAAkB;IAC1C,iBAAiB,EAAE,EAAE;IACrB,SAAS,EAAE,EAAE;IACb,cAAc,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"health-score.js","sourceRoot":"","sources":["../src/health-score.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,eAAe,GAAkC;IACrD,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,CAAC;CACR,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,sBAAsB,GAA2C;IACrE,SAAS,EAAE,EAAE,EAAW,kBAAkB;IAC1C,iBAAiB,EAAE,EAAE;IACrB,SAAS,EAAE,EAAE;IACb,cAAc,EAAE,EAAE;IAClB,SAAS,EAAE,EAAE,EAAW,kDAAkD;CAC3E,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAuB;IACxD,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAErD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;QAC5E,MAAM,GAAG,GAAG,sBAAsB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEnD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC5C,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;YACvD,KAAK,IAAI,MAAM,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,SAAS,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,aAAa,GAA8C;IAC/D,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAoB;IACjD,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAEtC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;IAExC,gCAAgC;IAChC,IACE,GAAG,KAAK,UAAU;QAClB,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACxB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC1B,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;QAC9B,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACxB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,yCAAyC;IACzC,IACE,GAAG,KAAK,kBAAkB;QAC1B,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QACvB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC;QAC5B,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QACjC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAC1B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,wBAAwB;IACxB,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACjH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8CAA8C;IAC9C,IACE,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC3B,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACxB,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC1B,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QAClC,KAAK,CAAC,QAAQ,CAAC,0BAA0B,CAAC,EAC1C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,6CAA6C;IAC7C,IACE,GAAG,KAAK,iBAAiB;QACzB,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;QAC9B,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QACvB,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC;QAClC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;QACzB,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAC5B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,yCAAyC;IACzC,IAAI,GAAG,KAAK,gBAAgB,IAAI,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9G,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,MAAuB;IACpD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvC,MAAM,UAAU,GACd,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1E,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,UAAU,CAAC;QAExC,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;QAC7C,IAAI,QAAQ,KAAK,CAAC;YAAE,OAAO,QAAQ,CAAC;QAEpC,MAAM,UAAU,GACd,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,UAAU,CAAC;QAExC,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC;QAC1D,IAAI,SAAS,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAEtC,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,OAAO,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAElC,OAAO,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IAEzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;YAAE,MAAM;QAC7B,gFAAgF;QAChF,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,SAAS;QACjD,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEnC,KAAK,CAAC,IAAI,CAAC;YACT,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,OAAO;YACrB,eAAe,EAAE,KAAK,CAAC,cAAc;SACtC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks / Settings Detector (issue #23)
|
|
3
|
+
*
|
|
4
|
+
* Audits `.claude/settings.json` and `.claude/settings.local.json`:
|
|
5
|
+
*
|
|
6
|
+
* - Invalid JSON: the file will not load at all.
|
|
7
|
+
* - Hook commands referencing local scripts that do not exist in the repo.
|
|
8
|
+
* - Dangerous hook patterns: piping a remote download straight into a shell
|
|
9
|
+
* (`curl … | sh`), and unquoted `$CLAUDE_PROJECT_DIR` path expansions.
|
|
10
|
+
*
|
|
11
|
+
* Hook commands run automatically on tool events, so a broken or unsafe command
|
|
12
|
+
* is higher-stakes than a stale doc line. Deterministic and offline.
|
|
13
|
+
*/
|
|
14
|
+
import type { RepoContext } from './repo-context.js';
|
|
15
|
+
import type { PromptCiIssue } from './types.js';
|
|
16
|
+
export declare function detectHooksSettings(context: RepoContext): PromptCiIssue[];
|
|
17
|
+
//# sourceMappingURL=hooks-settings-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-settings-detector.d.ts","sourceRoot":"","sources":["../src/hooks-settings-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA0IhD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,EAAE,CAyFzE"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks / Settings Detector (issue #23)
|
|
3
|
+
*
|
|
4
|
+
* Audits `.claude/settings.json` and `.claude/settings.local.json`:
|
|
5
|
+
*
|
|
6
|
+
* - Invalid JSON: the file will not load at all.
|
|
7
|
+
* - Hook commands referencing local scripts that do not exist in the repo.
|
|
8
|
+
* - Dangerous hook patterns: piping a remote download straight into a shell
|
|
9
|
+
* (`curl … | sh`), and unquoted `$CLAUDE_PROJECT_DIR` path expansions.
|
|
10
|
+
*
|
|
11
|
+
* Hook commands run automatically on tool events, so a broken or unsafe command
|
|
12
|
+
* is higher-stakes than a stale doc line. Deterministic and offline.
|
|
13
|
+
*/
|
|
14
|
+
import { readTextWithinRoot, isFileWithinRoot, shortHash, lineOf, withScannerPaths, SCRIPT_EXT_RE, aiConfigIssue as base, } from './ai-config.js';
|
|
15
|
+
/** curl/wget piped straight into a shell — the classic remote-code-execution footgun. */
|
|
16
|
+
const CURL_PIPE_SH_RE = /\b(curl|wget)\b[^|]*\|\s*(sudo\s+)?(sh|bash|zsh|dash)\b/i;
|
|
17
|
+
function id(kind, key) {
|
|
18
|
+
return `ai-config-hook-${kind}-${shortHash(key)}`;
|
|
19
|
+
}
|
|
20
|
+
/** Recursively collect `{ type: 'command', command }` hook entries, tagged with their event key. */
|
|
21
|
+
function collectCommandHooks(hooks, event, out) {
|
|
22
|
+
if (Array.isArray(hooks)) {
|
|
23
|
+
for (const item of hooks)
|
|
24
|
+
collectCommandHooks(item, event, out);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (typeof hooks !== 'object' || hooks === null)
|
|
28
|
+
return;
|
|
29
|
+
const obj = hooks;
|
|
30
|
+
if (obj.type === 'command' && typeof obj.command === 'string') {
|
|
31
|
+
out.push({ command: obj.command, event });
|
|
32
|
+
}
|
|
33
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
34
|
+
// When we recurse from the top-level `hooks` map, the key names the event
|
|
35
|
+
// (PreToolUse, PostToolUse, …); deeper down `hooks`/`matcher` keys are just
|
|
36
|
+
// structure, so we keep the nearest event label.
|
|
37
|
+
const nextEvent = event === '' ? key : event;
|
|
38
|
+
if (typeof value === 'object' && value !== null)
|
|
39
|
+
collectCommandHooks(value, nextEvent, out);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Interpreters whose script argument is the file they execute. */
|
|
43
|
+
const INTERPRETERS = new Set([
|
|
44
|
+
'bash', 'sh', 'zsh', 'dash', 'node', 'deno', 'python', 'python3', 'ruby',
|
|
45
|
+
'ts-node', 'tsx', 'pwsh', 'powershell',
|
|
46
|
+
]);
|
|
47
|
+
/** Launchers that wrap another command (`npx tsx x.ts`, `timeout 5 bash x.sh`). */
|
|
48
|
+
const WRAPPERS = new Set([
|
|
49
|
+
'npx', 'bunx', 'pnpm', 'yarn', 'uv', 'uvx', 'env', 'timeout', 'nice', 'sudo', 'exec', 'command',
|
|
50
|
+
]);
|
|
51
|
+
/** Strip surrounding quotes, then a leading `$CLAUDE_PROJECT_DIR/` / `${CLAUDE_PROJECT_DIR}/`. */
|
|
52
|
+
function normalizeScriptToken(token) {
|
|
53
|
+
const unquoted = token.replace(/^['"]|['"]$/g, '');
|
|
54
|
+
return unquoted.replace(/^\$\{?CLAUDE_PROJECT_DIR\}?[/\\]/, '');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Extract repo-relative local script references from a hook command string.
|
|
58
|
+
*
|
|
59
|
+
* Each `&&`/`||`/`;`/`|`-chained segment is its own command. Within a segment,
|
|
60
|
+
* the *executed script* is verified — the segment's own executable, or (after
|
|
61
|
+
* any interpreter/wrapper prefix like `bash`, `npx tsx`, `timeout 5 node`) the
|
|
62
|
+
* first argument bearing a script extension, which naturally skips flags and
|
|
63
|
+
* their extension-less values (`node --max-old-space-size 4096 scripts/x.js`).
|
|
64
|
+
* Other tokens are checked only when explicitly relative (`./x`, `.claude/x`,
|
|
65
|
+
* `$CLAUDE_PROJECT_DIR/x`), so a lint hook like `eslint --fix src/foo.js` does
|
|
66
|
+
* not get its target argument flagged as a missing script.
|
|
67
|
+
*/
|
|
68
|
+
function extractScriptRefs(command) {
|
|
69
|
+
const refs = new Set();
|
|
70
|
+
const add = (rawToken, executed) => {
|
|
71
|
+
if (rawToken.startsWith('-'))
|
|
72
|
+
return;
|
|
73
|
+
const unquoted = rawToken.replace(/^['"]|['"]$/g, '');
|
|
74
|
+
const token = normalizeScriptToken(rawToken);
|
|
75
|
+
if (!token || token.includes('$') || /[<>{}*?|"'`]/.test(token))
|
|
76
|
+
return; // still has a var/placeholder
|
|
77
|
+
if (token.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(token) || /^https?:/i.test(token))
|
|
78
|
+
return; // absolute/url
|
|
79
|
+
if (token.includes('..'))
|
|
80
|
+
return;
|
|
81
|
+
if (!SCRIPT_EXT_RE.test(token))
|
|
82
|
+
return;
|
|
83
|
+
// Tested on the quote-stripped form so `"./scripts/x.sh"` still counts.
|
|
84
|
+
const explicitRel = unquoted.startsWith('./') || unquoted.startsWith('.claude/') ||
|
|
85
|
+
/^\$\{?CLAUDE_PROJECT_DIR\}?[/\\]/.test(unquoted);
|
|
86
|
+
if (executed || explicitRel)
|
|
87
|
+
refs.add(token.replace(/\\/g, '/'));
|
|
88
|
+
};
|
|
89
|
+
for (const segment of command.split(/&&|\|\||;|\|/)) {
|
|
90
|
+
const tokens = segment.trim().split(/\s+/).filter(Boolean);
|
|
91
|
+
if (tokens.length === 0)
|
|
92
|
+
continue;
|
|
93
|
+
const first = normalizeScriptToken(tokens[0]);
|
|
94
|
+
if (INTERPRETERS.has(first) || WRAPPERS.has(first)) {
|
|
95
|
+
// The executed script is the first argument that looks like a script
|
|
96
|
+
// file; flags and their extension-less values are skipped naturally.
|
|
97
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
98
|
+
if (!tokens[i].startsWith('-') && SCRIPT_EXT_RE.test(normalizeScriptToken(tokens[i]))) {
|
|
99
|
+
add(tokens[i], true);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// The segment itself may be a direct script invocation (`./scripts/x.sh`).
|
|
106
|
+
add(tokens[0], true);
|
|
107
|
+
}
|
|
108
|
+
// Remaining tokens are verified only when explicitly relative.
|
|
109
|
+
for (let i = 1; i < tokens.length; i++)
|
|
110
|
+
add(tokens[i], false);
|
|
111
|
+
}
|
|
112
|
+
return [...refs];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* True when the character at `index` sits inside a single- or double-quoted
|
|
116
|
+
* region of `command`. Used so `sh -c "cd $CLAUDE_PROJECT_DIR/x"` — quoted,
|
|
117
|
+
* space-safe — is not flagged as an unquoted expansion.
|
|
118
|
+
*/
|
|
119
|
+
function insideQuotes(command, index) {
|
|
120
|
+
let quote = '';
|
|
121
|
+
for (let i = 0; i < index; i++) {
|
|
122
|
+
const c = command[i];
|
|
123
|
+
if (quote) {
|
|
124
|
+
if (c === quote)
|
|
125
|
+
quote = '';
|
|
126
|
+
}
|
|
127
|
+
else if (c === '"' || c === "'") {
|
|
128
|
+
quote = c;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return quote !== '';
|
|
132
|
+
}
|
|
133
|
+
/** Finds a `$CLAUDE_PROJECT_DIR/...` expansion that no quote region protects. */
|
|
134
|
+
function hasUnquotedProjectDir(command) {
|
|
135
|
+
for (const m of command.matchAll(/\$\{?CLAUDE_PROJECT_DIR\}?[/\\]/g)) {
|
|
136
|
+
if (!insideQuotes(command, m.index))
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
export function detectHooksSettings(context) {
|
|
142
|
+
const issues = [];
|
|
143
|
+
for (const filePath of context.aiConfig.settings) {
|
|
144
|
+
const raw = readTextWithinRoot(context.repoRoot, filePath);
|
|
145
|
+
if (raw === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
let parsed;
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(raw);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
issues.push(base({
|
|
153
|
+
id: id('invalid-json', filePath),
|
|
154
|
+
severity: 'high',
|
|
155
|
+
title: 'Settings file is not valid JSON',
|
|
156
|
+
summary: `${filePath} could not be parsed as JSON, so Claude Code cannot load its hooks or permissions.`,
|
|
157
|
+
filePaths: [filePath],
|
|
158
|
+
locations: [{ filePath, startLine: 1, endLine: 1 }],
|
|
159
|
+
evidence: [`Parse error: ${err.message}`],
|
|
160
|
+
recommendation: 'Fix the JSON syntax (trailing commas and comments are not allowed in settings.json).',
|
|
161
|
+
confidence: 0.95,
|
|
162
|
+
}));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
166
|
+
continue;
|
|
167
|
+
const hooks = parsed.hooks;
|
|
168
|
+
if (hooks === undefined)
|
|
169
|
+
continue;
|
|
170
|
+
const commandHooks = [];
|
|
171
|
+
collectCommandHooks(hooks, '', commandHooks);
|
|
172
|
+
const reportedMissing = new Set();
|
|
173
|
+
for (const { command, event } of commandHooks) {
|
|
174
|
+
const line = lineOf(raw, command);
|
|
175
|
+
// Dangerous: remote download piped into a shell.
|
|
176
|
+
if (CURL_PIPE_SH_RE.test(command)) {
|
|
177
|
+
issues.push(base({
|
|
178
|
+
id: id('curl-pipe-sh', `${filePath}|${command}`),
|
|
179
|
+
severity: 'high',
|
|
180
|
+
impact: 'high',
|
|
181
|
+
title: 'Hook pipes a remote download into a shell',
|
|
182
|
+
summary: `A ${event || 'hook'} command in ${filePath} pipes \`curl\`/\`wget\` output directly into a shell, running whatever the remote host serves with your permissions.`,
|
|
183
|
+
filePaths: [filePath],
|
|
184
|
+
locations: [{ filePath, startLine: line, endLine: line }],
|
|
185
|
+
evidence: [`Command: ${command.slice(0, 160)}`],
|
|
186
|
+
recommendation: 'Download to a file, review it, and pin a checksum before executing — never pipe a network fetch straight into sh/bash.',
|
|
187
|
+
confidence: 0.85,
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
// Footgun: unquoted $CLAUDE_PROJECT_DIR path expansion.
|
|
191
|
+
if (hasUnquotedProjectDir(command)) {
|
|
192
|
+
issues.push(base({
|
|
193
|
+
id: id('unquoted-project-dir', `${filePath}|${command}`),
|
|
194
|
+
severity: 'info',
|
|
195
|
+
title: 'Hook uses an unquoted $CLAUDE_PROJECT_DIR path',
|
|
196
|
+
summary: `A ${event || 'hook'} command in ${filePath} expands \`$CLAUDE_PROJECT_DIR\` unquoted; if the project path contains a space the command breaks or runs the wrong file.`,
|
|
197
|
+
filePaths: [filePath],
|
|
198
|
+
locations: [{ filePath, startLine: line, endLine: line }],
|
|
199
|
+
evidence: [`Command: ${command.slice(0, 160)}`],
|
|
200
|
+
recommendation: 'Wrap the path in double quotes, e.g. `"$CLAUDE_PROJECT_DIR/script.sh"`.',
|
|
201
|
+
confidence: 0.5,
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
// Missing local script references.
|
|
205
|
+
for (const ref of extractScriptRefs(command)) {
|
|
206
|
+
if (isFileWithinRoot(context.repoRoot, ref))
|
|
207
|
+
continue;
|
|
208
|
+
const key = `${filePath}|${ref}`;
|
|
209
|
+
if (reportedMissing.has(key))
|
|
210
|
+
continue;
|
|
211
|
+
reportedMissing.add(key);
|
|
212
|
+
issues.push(base({
|
|
213
|
+
id: id('missing-script', key),
|
|
214
|
+
title: 'Hook command references a script that does not exist',
|
|
215
|
+
summary: `A ${event || 'hook'} command in ${filePath} runs \`${ref}\`, but that file is not present in the repository.`,
|
|
216
|
+
filePaths: [filePath],
|
|
217
|
+
locations: [{ filePath, startLine: line, endLine: line }],
|
|
218
|
+
evidence: [`Referenced script: ${ref}`, `Command: ${command.slice(0, 160)}`],
|
|
219
|
+
recommendation: 'Add the script, fix the path, or remove the hook.',
|
|
220
|
+
confidence: 0.75,
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Scanner-form paths so inline suppressions can match (see withScannerPaths).
|
|
226
|
+
return withScannerPaths(context.repoRoot, issues);
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=hooks-settings-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-settings-detector.js","sourceRoot":"","sources":["../src/hooks-settings-detector.ts"],"names":[],"mappings":"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"}
|