@lhi/tdd-audit 1.1.0 → 1.1.1
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/index.js +31 -14
- package/package.json +1 -1
- package/prompts/auto-audit.md +1 -1
package/index.js
CHANGED
|
@@ -16,7 +16,6 @@ const projectDir = process.cwd();
|
|
|
16
16
|
|
|
17
17
|
const targetSkillDir = path.join(agentBaseDir, agentDirName, 'skills', 'tdd-remediation');
|
|
18
18
|
const targetWorkflowDir = path.join(agentBaseDir, agentDirName, 'workflows');
|
|
19
|
-
const targetTestDir = path.join(projectDir, '__tests__', 'security');
|
|
20
19
|
|
|
21
20
|
// ─── 1. Framework Detection ──────────────────────────────────────────────────
|
|
22
21
|
|
|
@@ -43,7 +42,24 @@ function detectFramework() {
|
|
|
43
42
|
|
|
44
43
|
const framework = detectFramework();
|
|
45
44
|
|
|
46
|
-
// ─── 2.
|
|
45
|
+
// ─── 2. Test Directory Detection ─────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
function detectTestBaseDir() {
|
|
48
|
+
// Respect an existing convention before inventing one
|
|
49
|
+
const candidates = ['__tests__', 'tests', 'test', 'spec'];
|
|
50
|
+
for (const dir of candidates) {
|
|
51
|
+
if (fs.existsSync(path.join(projectDir, dir))) return dir;
|
|
52
|
+
}
|
|
53
|
+
// Framework-informed defaults when no directory exists yet
|
|
54
|
+
if (framework === 'pytest') return 'tests';
|
|
55
|
+
if (framework === 'go') return 'test';
|
|
56
|
+
return '__tests__';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const testBaseDir = detectTestBaseDir();
|
|
60
|
+
const targetTestDir = path.join(projectDir, testBaseDir, 'security');
|
|
61
|
+
|
|
62
|
+
// ─── 3. Quick Scan ───────────────────────────────────────────────────────────
|
|
47
63
|
|
|
48
64
|
const VULN_PATTERNS = [
|
|
49
65
|
{ name: 'SQL Injection', severity: 'CRITICAL', pattern: /(`SELECT[^`]*\$\{|"SELECT[^"]*"\s*\+|execute\(f"|cursor\.execute\(.*%s|\.query\(`[^`]*\$\{)/i },
|
|
@@ -111,9 +127,9 @@ function printFindings(findings) {
|
|
|
111
127
|
console.log('\n Run /tdd-audit in your agent to remediate.\n');
|
|
112
128
|
}
|
|
113
129
|
|
|
114
|
-
// ───
|
|
130
|
+
// ─── 4. Install Skill Files ───────────────────────────────────────────────────
|
|
115
131
|
|
|
116
|
-
console.log(`\nInstalling TDD Remediation Skill (${isLocal ? 'local' : 'global'}, framework: ${framework})...\n`);
|
|
132
|
+
console.log(`\nInstalling TDD Remediation Skill (${isLocal ? 'local' : 'global'}, framework: ${framework}, test dir: ${testBaseDir}/)...\n`);
|
|
117
133
|
|
|
118
134
|
if (!fs.existsSync(targetSkillDir)) fs.mkdirSync(targetSkillDir, { recursive: true });
|
|
119
135
|
|
|
@@ -123,7 +139,7 @@ for (const item of ['SKILL.md', 'prompts', 'templates']) {
|
|
|
123
139
|
if (fs.existsSync(src)) fs.cpSync(src, dest, { recursive: true });
|
|
124
140
|
}
|
|
125
141
|
|
|
126
|
-
// ───
|
|
142
|
+
// ─── 5. Scaffold Security Test Boilerplate ────────────────────────────────────
|
|
127
143
|
|
|
128
144
|
if (!fs.existsSync(targetTestDir)) {
|
|
129
145
|
fs.mkdirSync(targetTestDir, { recursive: true });
|
|
@@ -147,7 +163,7 @@ if (!fs.existsSync(destTest) && fs.existsSync(srcTest)) {
|
|
|
147
163
|
console.log(`✅ Scaffolded ${path.relative(projectDir, destTest)}`);
|
|
148
164
|
}
|
|
149
165
|
|
|
150
|
-
// ───
|
|
166
|
+
// ─── 6. Install Workflow Shortcode ────────────────────────────────────────────
|
|
151
167
|
|
|
152
168
|
if (!fs.existsSync(targetWorkflowDir)) fs.mkdirSync(targetWorkflowDir, { recursive: true });
|
|
153
169
|
const srcWorkflow = path.join(__dirname, 'workflows', 'tdd-audit.md');
|
|
@@ -157,7 +173,7 @@ if (fs.existsSync(srcWorkflow)) {
|
|
|
157
173
|
console.log(`✅ Installed /tdd-audit workflow shortcode`);
|
|
158
174
|
}
|
|
159
175
|
|
|
160
|
-
// ───
|
|
176
|
+
// ─── 7. Inject test:security into package.json ────────────────────────────────
|
|
161
177
|
|
|
162
178
|
const pkgPath = path.join(projectDir, 'package.json');
|
|
163
179
|
if (framework !== 'pytest' && framework !== 'go' && fs.existsSync(pkgPath)) {
|
|
@@ -165,11 +181,12 @@ if (framework !== 'pytest' && framework !== 'go' && fs.existsSync(pkgPath)) {
|
|
|
165
181
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
166
182
|
if (!pkg.scripts?.['test:security']) {
|
|
167
183
|
pkg.scripts = pkg.scripts || {};
|
|
184
|
+
const secDir = `${testBaseDir}/security`;
|
|
168
185
|
pkg.scripts['test:security'] = {
|
|
169
|
-
jest:
|
|
170
|
-
vitest:
|
|
171
|
-
mocha:
|
|
172
|
-
}[framework] ||
|
|
186
|
+
jest: `jest --testPathPattern=${secDir} --forceExit`,
|
|
187
|
+
vitest: `vitest run ${secDir}`,
|
|
188
|
+
mocha: `mocha '${secDir}/**/*.spec.js'`,
|
|
189
|
+
}[framework] || `jest --testPathPattern=${secDir} --forceExit`;
|
|
173
190
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
174
191
|
console.log(`✅ Added "test:security" script to package.json`);
|
|
175
192
|
} else {
|
|
@@ -180,7 +197,7 @@ if (framework !== 'pytest' && framework !== 'go' && fs.existsSync(pkgPath)) {
|
|
|
180
197
|
}
|
|
181
198
|
}
|
|
182
199
|
|
|
183
|
-
// ───
|
|
200
|
+
// ─── 8. Scaffold CI Workflow ─────────────────────────────────────────────────
|
|
184
201
|
|
|
185
202
|
const ciWorkflowDir = path.join(projectDir, '.github', 'workflows');
|
|
186
203
|
const ciWorkflowPath = path.join(ciWorkflowDir, 'security-tests.yml');
|
|
@@ -203,7 +220,7 @@ if (!fs.existsSync(ciWorkflowPath)) {
|
|
|
203
220
|
console.log(` .github/workflows/security-tests.yml already exists — skipped`);
|
|
204
221
|
}
|
|
205
222
|
|
|
206
|
-
// ───
|
|
223
|
+
// ─── 9. Pre-commit Hook (opt-in) ─────────────────────────────────────────────
|
|
207
224
|
|
|
208
225
|
if (withHooks) {
|
|
209
226
|
const gitDir = path.join(projectDir, '.git');
|
|
@@ -241,7 +258,7 @@ if (withHooks) {
|
|
|
241
258
|
}
|
|
242
259
|
}
|
|
243
260
|
|
|
244
|
-
// ───
|
|
261
|
+
// ─── 10. Quick Scan ──────────────────────────────────────────────────────────
|
|
245
262
|
|
|
246
263
|
if (!skipScan) {
|
|
247
264
|
process.stdout.write('\n🔍 Scanning for vulnerability patterns...');
|
package/package.json
CHANGED
package/prompts/auto-audit.md
CHANGED
|
@@ -101,7 +101,7 @@ Ask the user to confirm the list before beginning remediation. If they say "fix
|
|
|
101
101
|
|
|
102
102
|
For **each** confirmed vulnerability, rigorously apply the RED-GREEN-REFACTOR protocol in order:
|
|
103
103
|
|
|
104
|
-
1. **[RED](./red-phase.md)**: Write the exploit test in `__tests__/security/` and run it to prove the vulnerability exists (test must fail).
|
|
104
|
+
1. **[RED](./red-phase.md)**: Write the exploit test in the project's security test directory (e.g., `tests/security/`, `__tests__/security/`, `test/security/` — wherever the installer scaffolded the boilerplate) and run it to prove the vulnerability exists (test must fail).
|
|
105
105
|
2. **[GREEN](./green-phase.md)**: Apply the targeted patch. Run the exploit test — it must now pass.
|
|
106
106
|
3. **[REFACTOR](./refactor-phase.md)**: Run the full test suite. All tests must be green before moving on.
|
|
107
107
|
|