@bouncesecurity/aghast 0.0.13

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.
Files changed (97) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +111 -0
  3. package/config/prompts/generic-instructions.md +56 -0
  4. package/config/prompts/test-cheaper-instructions.md +57 -0
  5. package/dist/check-library.d.ts +87 -0
  6. package/dist/check-library.d.ts.map +1 -0
  7. package/dist/check-library.js +374 -0
  8. package/dist/check-library.js.map +1 -0
  9. package/dist/claude-code-provider.d.ts +26 -0
  10. package/dist/claude-code-provider.d.ts.map +1 -0
  11. package/dist/claude-code-provider.js +247 -0
  12. package/dist/claude-code-provider.js.map +1 -0
  13. package/dist/cli.d.ts +13 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +78 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/colors.d.ts +7 -0
  18. package/dist/colors.d.ts.map +1 -0
  19. package/dist/colors.js +18 -0
  20. package/dist/colors.js.map +1 -0
  21. package/dist/error-codes.d.ts +42 -0
  22. package/dist/error-codes.d.ts.map +1 -0
  23. package/dist/error-codes.js +60 -0
  24. package/dist/error-codes.js.map +1 -0
  25. package/dist/formatters/index.d.ts +10 -0
  26. package/dist/formatters/index.d.ts.map +1 -0
  27. package/dist/formatters/index.js +23 -0
  28. package/dist/formatters/index.js.map +1 -0
  29. package/dist/formatters/json-formatter.d.ts +11 -0
  30. package/dist/formatters/json-formatter.d.ts.map +1 -0
  31. package/dist/formatters/json-formatter.js +11 -0
  32. package/dist/formatters/json-formatter.js.map +1 -0
  33. package/dist/formatters/sarif-formatter.d.ts +18 -0
  34. package/dist/formatters/sarif-formatter.d.ts.map +1 -0
  35. package/dist/formatters/sarif-formatter.js +103 -0
  36. package/dist/formatters/sarif-formatter.js.map +1 -0
  37. package/dist/formatters/types.d.ts +11 -0
  38. package/dist/formatters/types.d.ts.map +1 -0
  39. package/dist/formatters/types.js +6 -0
  40. package/dist/formatters/types.js.map +1 -0
  41. package/dist/index.d.ts +7 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +406 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/logging.d.ts +26 -0
  46. package/dist/logging.d.ts.map +1 -0
  47. package/dist/logging.js +79 -0
  48. package/dist/logging.js.map +1 -0
  49. package/dist/mock-ai-provider.d.ts +18 -0
  50. package/dist/mock-ai-provider.d.ts.map +1 -0
  51. package/dist/mock-ai-provider.js +28 -0
  52. package/dist/mock-ai-provider.js.map +1 -0
  53. package/dist/new-check.d.ts +13 -0
  54. package/dist/new-check.d.ts.map +1 -0
  55. package/dist/new-check.js +405 -0
  56. package/dist/new-check.js.map +1 -0
  57. package/dist/prompt-template.d.ts +12 -0
  58. package/dist/prompt-template.d.ts.map +1 -0
  59. package/dist/prompt-template.js +35 -0
  60. package/dist/prompt-template.js.map +1 -0
  61. package/dist/provider-registry.d.ts +15 -0
  62. package/dist/provider-registry.d.ts.map +1 -0
  63. package/dist/provider-registry.js +27 -0
  64. package/dist/provider-registry.js.map +1 -0
  65. package/dist/repository-analyzer.d.ts +68 -0
  66. package/dist/repository-analyzer.d.ts.map +1 -0
  67. package/dist/repository-analyzer.js +230 -0
  68. package/dist/repository-analyzer.js.map +1 -0
  69. package/dist/response-parser.d.ts +12 -0
  70. package/dist/response-parser.d.ts.map +1 -0
  71. package/dist/response-parser.js +109 -0
  72. package/dist/response-parser.js.map +1 -0
  73. package/dist/runtime-config.d.ts +15 -0
  74. package/dist/runtime-config.d.ts.map +1 -0
  75. package/dist/runtime-config.js +73 -0
  76. package/dist/runtime-config.js.map +1 -0
  77. package/dist/sarif-parser.d.ts +20 -0
  78. package/dist/sarif-parser.d.ts.map +1 -0
  79. package/dist/sarif-parser.js +76 -0
  80. package/dist/sarif-parser.js.map +1 -0
  81. package/dist/scan-runner.d.ts +29 -0
  82. package/dist/scan-runner.d.ts.map +1 -0
  83. package/dist/scan-runner.js +559 -0
  84. package/dist/scan-runner.js.map +1 -0
  85. package/dist/semgrep-runner.d.ts +25 -0
  86. package/dist/semgrep-runner.d.ts.map +1 -0
  87. package/dist/semgrep-runner.js +100 -0
  88. package/dist/semgrep-runner.js.map +1 -0
  89. package/dist/snippet-extractor.d.ts +25 -0
  90. package/dist/snippet-extractor.d.ts.map +1 -0
  91. package/dist/snippet-extractor.js +56 -0
  92. package/dist/snippet-extractor.js.map +1 -0
  93. package/dist/types.d.ts +206 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +19 -0
  96. package/dist/types.js.map +1 -0
  97. package/package.json +55 -0
@@ -0,0 +1,79 @@
1
+ const LOG_PRIORITY = {
2
+ silent: 0,
3
+ info: 1,
4
+ debug: 2,
5
+ };
6
+ let cachedLogLevel;
7
+ export function getLogLevel() {
8
+ if (cachedLogLevel)
9
+ return cachedLogLevel;
10
+ cachedLogLevel = 'info';
11
+ return cachedLogLevel;
12
+ }
13
+ /**
14
+ * Programmatically set the log level (avoids env var race conditions with --debug flag).
15
+ */
16
+ export function setLogLevel(level) {
17
+ cachedLogLevel = level;
18
+ }
19
+ function formatTimestamp() {
20
+ return new Date().toISOString().replace('T', ' ').replace('Z', '');
21
+ }
22
+ /**
23
+ * Log a progress/activity message at info level.
24
+ */
25
+ export function logProgress(tag, message, details) {
26
+ if (LOG_PRIORITY['info'] <= LOG_PRIORITY[getLogLevel()]) {
27
+ const timestamp = formatTimestamp();
28
+ const detailStr = details ? ` ${JSON.stringify(details)}` : '';
29
+ console.log(`${timestamp} [${tag}] ${message}${detailStr}`);
30
+ }
31
+ }
32
+ /**
33
+ * Create a timer for measuring elapsed time.
34
+ */
35
+ export function createTimer() {
36
+ const start = Date.now();
37
+ return {
38
+ elapsed: () => Date.now() - start,
39
+ elapsedStr: () => {
40
+ const ms = Date.now() - start;
41
+ if (ms < 1000)
42
+ return `${ms}ms`;
43
+ if (ms < 60000)
44
+ return `${(ms / 1000).toFixed(1)}s`;
45
+ return `${(ms / 60000).toFixed(1)}m`;
46
+ },
47
+ };
48
+ }
49
+ /**
50
+ * Log debug information (single line, compact).
51
+ */
52
+ export function logDebug(tag, message, data) {
53
+ if (getLogLevel() !== 'debug')
54
+ return;
55
+ const timestamp = formatTimestamp();
56
+ if (data === undefined) {
57
+ console.log(`${timestamp} [${tag}][debug] ${message}`);
58
+ }
59
+ else {
60
+ const formatted = typeof data === 'string' ? data : JSON.stringify(data);
61
+ const truncated = formatted.length > 200 ? formatted.slice(0, 200) + '...' : formatted;
62
+ console.log(`${timestamp} [${tag}][debug] ${message}: ${truncated}`);
63
+ }
64
+ }
65
+ /**
66
+ * Log debug information without truncation (for full prompts and responses).
67
+ */
68
+ export function logDebugFull(tag, message, data) {
69
+ if (getLogLevel() !== 'debug')
70
+ return;
71
+ const timestamp = formatTimestamp();
72
+ if (data === undefined) {
73
+ console.log(`${timestamp} [${tag}][debug] ${message}`);
74
+ }
75
+ else {
76
+ console.log(`${timestamp} [${tag}][debug] ${message}:\n${data}`);
77
+ }
78
+ }
79
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Lightweight mock AI provider for CLI `AGHAST_MOCK_AI` mode.
3
+ *
4
+ * Returns a fixed raw response without calling any AI API.
5
+ * This is shipped with the package (unlike the full test mock in tests/mocks/).
6
+ */
7
+ import type { AIProvider, AIResponse, ProviderConfig } from './types.js';
8
+ export declare class MockAIProvider implements AIProvider {
9
+ private rawResponse;
10
+ constructor(options: {
11
+ rawResponse: string;
12
+ });
13
+ initialize(_config: ProviderConfig): Promise<void>;
14
+ executeCheck(_instructions: string, _repositoryPath: string, _logPrefix?: string): Promise<AIResponse>;
15
+ validateConfig(): Promise<boolean>;
16
+ enableDebug(): void;
17
+ }
18
+ //# sourceMappingURL=mock-ai-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-ai-provider.d.ts","sourceRoot":"","sources":["../src/mock-ai-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzE,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE;IAItC,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,YAAY,CAChB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EACvB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC;IAOhB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxC,WAAW,IAAI,IAAI;CAGpB"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Lightweight mock AI provider for CLI `AGHAST_MOCK_AI` mode.
3
+ *
4
+ * Returns a fixed raw response without calling any AI API.
5
+ * This is shipped with the package (unlike the full test mock in tests/mocks/).
6
+ */
7
+ export class MockAIProvider {
8
+ rawResponse;
9
+ constructor(options) {
10
+ this.rawResponse = options.rawResponse;
11
+ }
12
+ async initialize(_config) {
13
+ // No-op
14
+ }
15
+ async executeCheck(_instructions, _repositoryPath, _logPrefix) {
16
+ return {
17
+ raw: this.rawResponse,
18
+ parsed: undefined,
19
+ };
20
+ }
21
+ async validateConfig() {
22
+ return true;
23
+ }
24
+ enableDebug() {
25
+ // No-op
26
+ }
27
+ }
28
+ //# sourceMappingURL=mock-ai-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-ai-provider.js","sourceRoot":"","sources":["../src/mock-ai-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,OAAO,cAAc;IACjB,WAAW,CAAS;IAE5B,YAAY,OAAgC;QAC1C,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAuB;QACtC,QAAQ;IACV,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,aAAqB,EACrB,eAAuB,EACvB,UAAmB;QAEnB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,WAAW;YACrB,MAAM,EAAE,SAAS;SAClB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,WAAW;QACT,QAAQ;IACV,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * CLI utility for scaffolding new security checks.
3
+ * Creates a check folder with check.json (Layer 2), instructions.md,
4
+ * and optionally rule.yaml + tests/ for Semgrep checks.
5
+ * Appends a registry entry to checks-config.json (Layer 1).
6
+ *
7
+ * Usage:
8
+ * pnpm exec tsx src/new-check.ts # Interactive mode
9
+ * pnpm exec tsx src/new-check.ts --id aghast-xss # Mixed mode (prompts for missing)
10
+ * pnpm exec tsx src/new-check.ts --id ... --name ... # Full flag mode (no prompts)
11
+ */
12
+ export declare function runNewCheck(args: string[]): Promise<void>;
13
+ //# sourceMappingURL=new-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-check.d.ts","sourceRoot":"","sources":["../src/new-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA2YH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyF/D"}
@@ -0,0 +1,405 @@
1
+ /**
2
+ * CLI utility for scaffolding new security checks.
3
+ * Creates a check folder with check.json (Layer 2), instructions.md,
4
+ * and optionally rule.yaml + tests/ for Semgrep checks.
5
+ * Appends a registry entry to checks-config.json (Layer 1).
6
+ *
7
+ * Usage:
8
+ * pnpm exec tsx src/new-check.ts # Interactive mode
9
+ * pnpm exec tsx src/new-check.ts --id aghast-xss # Mixed mode (prompts for missing)
10
+ * pnpm exec tsx src/new-check.ts --id ... --name ... # Full flag mode (no prompts)
11
+ */
12
+ import { readFile, writeFile, access, mkdir } from 'node:fs/promises';
13
+ import { resolve } from 'node:path';
14
+ import { createInterface } from 'node:readline/promises';
15
+ import { stdin, stdout } from 'node:process';
16
+ import { createRequire } from 'node:module';
17
+ import { ERROR_CODES, formatError, formatFatalError } from './error-codes.js';
18
+ const ID_PREFIX = 'aghast-';
19
+ const SUPPORTED_LANGUAGES = {
20
+ python: { semgrepId: 'python', extension: '.py', commentPrefix: '#' },
21
+ py: { semgrepId: 'python', extension: '.py', commentPrefix: '#' },
22
+ javascript: { semgrepId: 'javascript', extension: '.js', commentPrefix: '//' },
23
+ js: { semgrepId: 'javascript', extension: '.js', commentPrefix: '//' },
24
+ typescript: { semgrepId: 'typescript', extension: '.ts', commentPrefix: '//' },
25
+ ts: { semgrepId: 'typescript', extension: '.ts', commentPrefix: '//' },
26
+ };
27
+ // Canonical names for display in prompts
28
+ const LANGUAGE_CHOICES = ['python', 'javascript', 'typescript'];
29
+ const CLI_FLAG_MAP = {
30
+ '--id': 'id',
31
+ '--name': 'name',
32
+ '--severity': 'severity',
33
+ '--confidence': 'confidence',
34
+ '--repositories': 'repositories',
35
+ '--check-overview': 'checkOverview',
36
+ '--check-items': 'checkItems',
37
+ '--pass-condition': 'passCondition',
38
+ '--fail-condition': 'failCondition',
39
+ '--flag-condition': 'flagCondition',
40
+ '--check-type': 'checkType',
41
+ '--semgrep-rules': 'semgrepRules',
42
+ '--max-targets': 'maxTargets',
43
+ '--language': 'language',
44
+ '--config-dir': 'configDir',
45
+ };
46
+ function parseFlags(args) {
47
+ const flags = {};
48
+ for (let i = 0; i < args.length; i++) {
49
+ const key = CLI_FLAG_MAP[args[i]];
50
+ if (key) {
51
+ const value = args[i + 1];
52
+ if (value === undefined || value.startsWith('--')) {
53
+ console.error(formatError(ERROR_CODES.E1001, `${args[i]} requires a value`));
54
+ process.exit(1);
55
+ }
56
+ flags[key] = value;
57
+ i++; // skip value
58
+ }
59
+ }
60
+ return flags;
61
+ }
62
+ // --- Interactive Prompts ---
63
+ async function promptForMissing(flags) {
64
+ const needsPrompt = !flags.id || !flags.name ||
65
+ !flags.checkOverview || !flags.checkItems ||
66
+ !flags.passCondition || !flags.failCondition ||
67
+ !flags.checkType;
68
+ let rl;
69
+ if (needsPrompt) {
70
+ rl = createInterface({ input: stdin, output: stdout });
71
+ }
72
+ async function ask(label, existing) {
73
+ if (existing !== undefined)
74
+ return existing;
75
+ if (!rl)
76
+ throw new Error('Unexpected: readline not initialized');
77
+ const maxAttempts = 3;
78
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
79
+ const answer = await rl.question(`${label}: `);
80
+ if (answer.trim())
81
+ return answer.trim();
82
+ const remaining = maxAttempts - attempt;
83
+ if (remaining > 0) {
84
+ console.error(formatError(ERROR_CODES.E1003, `${label} is required (${remaining} ${remaining === 1 ? 'attempt' : 'attempts'} remaining)`));
85
+ }
86
+ }
87
+ console.error(formatError(ERROR_CODES.E1003, `${label} is required — no valid input after ${maxAttempts} attempts`));
88
+ rl.close();
89
+ process.exit(1);
90
+ }
91
+ async function askOptional(label, existing) {
92
+ if (existing !== undefined)
93
+ return existing;
94
+ if (!rl)
95
+ return '';
96
+ const answer = await rl.question(`${label} (optional, press Enter to skip): `);
97
+ return answer.trim();
98
+ }
99
+ async function askWithDefault(label, defaultValue, existing) {
100
+ if (existing !== undefined)
101
+ return existing;
102
+ if (!rl)
103
+ return defaultValue;
104
+ const answer = await rl.question(`${label} [${defaultValue}]: `);
105
+ return answer.trim() || defaultValue;
106
+ }
107
+ const result = {
108
+ id: await ask('Check ID (e.g. aghast-xss)', flags.id),
109
+ name: await ask('Check name (e.g. XSS Prevention)', flags.name),
110
+ severity: await askOptional('Severity (critical/high/medium/low/informational)', flags.severity),
111
+ confidence: await askOptional('Confidence (high/medium/low)', flags.confidence),
112
+ repositories: flags.repositories !== undefined ? flags.repositories : await askOptional('Repositories (comma-separated URLs, or empty for all)', undefined),
113
+ checkOverview: await ask('Check overview / description', flags.checkOverview),
114
+ checkItems: await ask('Check items (comma-separated)', flags.checkItems),
115
+ passCondition: await ask('PASS condition', flags.passCondition),
116
+ failCondition: await ask('FAIL condition', flags.failCondition),
117
+ flagCondition: await askOptional('FLAG condition (requires human investigation)', flags.flagCondition),
118
+ checkType: await askWithDefault('Check type (repository/semgrep/semgrep-only)', 'repository', flags.checkType),
119
+ semgrepRules: '',
120
+ maxTargets: '',
121
+ language: '',
122
+ };
123
+ if (result.checkType === 'semgrep' || result.checkType === 'semgrep-only') {
124
+ result.semgrepRules = flags.semgrepRules !== undefined
125
+ ? flags.semgrepRules
126
+ : await askOptional('Semgrep rule file paths (comma-separated, or press Enter to generate template)', undefined);
127
+ result.maxTargets = await askOptional('Max targets', flags.maxTargets);
128
+ if (!result.semgrepRules) {
129
+ // Language is required when generating a rule template + test file
130
+ result.language = await ask(`Language (${LANGUAGE_CHOICES.join('/')})`, flags.language);
131
+ }
132
+ else {
133
+ result.language = flags.language ?? '';
134
+ }
135
+ }
136
+ if (rl)
137
+ rl.close();
138
+ return result;
139
+ }
140
+ // --- ID Prefix ---
141
+ function ensurePrefix(id) {
142
+ if (id.startsWith(ID_PREFIX))
143
+ return id;
144
+ return ID_PREFIX + id;
145
+ }
146
+ async function loadExistingRegistry(registryPath) {
147
+ const raw = await readFile(registryPath, 'utf-8');
148
+ return JSON.parse(raw);
149
+ }
150
+ function validateInputs(inputs, registry) {
151
+ const errors = [];
152
+ if (!inputs.id) {
153
+ errors.push('Check ID is required');
154
+ }
155
+ if (registry.checks.some((c) => c.id === inputs.id)) {
156
+ errors.push(`Check ID "${inputs.id}" already exists in config`);
157
+ }
158
+ const validSeverities = ['critical', 'high', 'medium', 'low', 'informational'];
159
+ if (inputs.severity && !validSeverities.includes(inputs.severity)) {
160
+ errors.push(`Invalid severity "${inputs.severity}". Must be one of: ${validSeverities.join(', ')}`);
161
+ }
162
+ const validConfidences = ['high', 'medium', 'low'];
163
+ if (inputs.confidence && !validConfidences.includes(inputs.confidence)) {
164
+ errors.push(`Invalid confidence "${inputs.confidence}". Must be one of: ${validConfidences.join(', ')}`);
165
+ }
166
+ const validCheckTypes = ['repository', 'semgrep', 'semgrep-only', ''];
167
+ if (!validCheckTypes.includes(inputs.checkType)) {
168
+ errors.push(`Invalid check type "${inputs.checkType}". Must be one of: repository, semgrep, semgrep-only`);
169
+ }
170
+ if (inputs.maxTargets) {
171
+ const parsed = parseInt(inputs.maxTargets, 10);
172
+ if (isNaN(parsed) || parsed <= 0) {
173
+ errors.push(`Invalid maxTargets "${inputs.maxTargets}". Must be a positive integer`);
174
+ }
175
+ }
176
+ if ((inputs.checkType === 'semgrep' || inputs.checkType === 'semgrep-only') && inputs.language && !SUPPORTED_LANGUAGES[inputs.language]) {
177
+ errors.push(`Invalid language "${inputs.language}". Must be one of: ${Object.keys(SUPPORTED_LANGUAGES).join(', ')}`);
178
+ }
179
+ return errors;
180
+ }
181
+ async function checkFileExists(path) {
182
+ try {
183
+ await access(path);
184
+ return true;
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
190
+ // --- Semgrep Rule Template ---
191
+ function generateSemgrepRule(checkId, language) {
192
+ const langInfo = SUPPORTED_LANGUAGES[language];
193
+ const semgrepLang = langInfo ? langInfo.semgrepId : 'javascript';
194
+ return `rules:
195
+ - id: ${checkId}
196
+ pattern: |
197
+ # TODO: Replace with your Semgrep pattern
198
+ ...
199
+ message: >
200
+ TODO: Describe the issue this rule detects.
201
+ languages: [${semgrepLang}]
202
+ severity: WARNING
203
+ `;
204
+ }
205
+ function generateSemgrepTestFile(checkId, language) {
206
+ const langInfo = SUPPORTED_LANGUAGES[language];
207
+ const comment = langInfo ? langInfo.commentPrefix : '#';
208
+ return `${comment} ruleid: ${checkId}
209
+ ${comment} TODO: Add code that SHOULD be matched by the rule
210
+
211
+ ${comment} ok: ${checkId}
212
+ ${comment} TODO: Add code that should NOT be matched by the rule
213
+ `;
214
+ }
215
+ // --- File Generation ---
216
+ function generateMarkdown(inputs) {
217
+ const items = inputs.checkItems
218
+ .split(',')
219
+ .map((item) => item.trim())
220
+ .filter(Boolean);
221
+ const itemLines = items.map((item, i) => `${i + 1}. ${item}`).join('\n');
222
+ let resultLines = `- **PASS**: ${inputs.passCondition}\n- **FAIL**: ${inputs.failCondition}`;
223
+ if (inputs.flagCondition) {
224
+ resultLines += `\n- **FLAG**: ${inputs.flagCondition}`;
225
+ }
226
+ return `### ${inputs.name}
227
+
228
+ #### Overview
229
+ ${inputs.checkOverview}
230
+
231
+ #### What to Check
232
+ ${itemLines}
233
+
234
+ #### Result
235
+ ${resultLines}
236
+ `;
237
+ }
238
+ function generateCheckDefinition(inputs) {
239
+ const def = {
240
+ id: inputs.id,
241
+ name: inputs.name,
242
+ };
243
+ // semgrep-only checks don't have an instructionsFile
244
+ if (inputs.checkType !== 'semgrep-only') {
245
+ def.instructionsFile = `${inputs.id}.md`;
246
+ }
247
+ if (inputs.severity) {
248
+ def.severity = inputs.severity;
249
+ }
250
+ if (inputs.confidence) {
251
+ def.confidence = inputs.confidence;
252
+ }
253
+ if (inputs.checkType === 'semgrep' || inputs.checkType === 'semgrep-only') {
254
+ const checkTarget = { type: inputs.checkType };
255
+ if (inputs.semgrepRules) {
256
+ const rules = inputs.semgrepRules.split(',').map((r) => r.trim()).filter(Boolean);
257
+ checkTarget.rules = rules.length === 1 ? rules[0] : rules;
258
+ }
259
+ else {
260
+ checkTarget.rules = `${inputs.id}.yaml`;
261
+ }
262
+ if (inputs.maxTargets) {
263
+ checkTarget.maxTargets = parseInt(inputs.maxTargets, 10);
264
+ }
265
+ def.checkTarget = checkTarget;
266
+ }
267
+ return def;
268
+ }
269
+ function generateRegistryEntry(inputs) {
270
+ return {
271
+ id: inputs.id,
272
+ repositories: inputs.repositories
273
+ ? inputs.repositories.split(',').map((r) => r.trim()).filter(Boolean)
274
+ : [],
275
+ enabled: true,
276
+ };
277
+ }
278
+ // --- Main ---
279
+ const NEW_CHECK_HELP = `Usage: aghast new-check --config-dir <path> [options]
280
+
281
+ Scaffold a new security check. Runs interactively by default, prompting for any
282
+ values not provided via flags. If the config directory does not exist, it will be
283
+ created with an empty checks-config.json.
284
+
285
+ Options:
286
+ --config-dir <path> Config directory containing checks-config.json and
287
+ checks/ folder. Created if it does not exist.
288
+ Required unless AGHAST_CONFIG_DIR is set.
289
+ --id <id> Check ID (will be prefixed with "aghast-" if needed)
290
+ --name <name> Human-readable check name (e.g. "XSS Prevention")
291
+ --severity <level> Severity: critical, high, medium, low, informational
292
+ --confidence <level> Confidence: high, medium, low
293
+ --repositories <urls> Comma-separated repository URLs (empty = all repos)
294
+ --check-overview <text> Description of what this check does
295
+ --check-items <items> Comma-separated list of things to check
296
+ --pass-condition <text> Condition for a PASS result
297
+ --fail-condition <text> Condition for a FAIL result
298
+ --flag-condition <text> Condition for a FLAG result (optional)
299
+ --check-type <type> Check type: repository (default), semgrep, semgrep-only
300
+ --semgrep-rules <paths> Comma-separated Semgrep rule file paths
301
+ --max-targets <n> Maximum number of Semgrep targets to analyze
302
+ --language <lang> Language for Semgrep template: python, javascript, typescript
303
+ -h, --help Show this help message
304
+
305
+ Environment variables:
306
+ AGHAST_CONFIG_DIR Default config directory (CLI --config-dir takes precedence)
307
+
308
+ Check types:
309
+ repository AI analyzes the whole repository (no Semgrep)
310
+ semgrep Semgrep finds targets, AI analyzes each one
311
+ semgrep-only Semgrep findings mapped directly to issues (no AI)
312
+
313
+ Examples:
314
+ aghast new-check --config-dir ./my-checks
315
+ aghast new-check --config-dir ./my-checks --id xss --name "XSS Prevention"
316
+ aghast new-check --config-dir ./my-checks --check-type semgrep --language typescript`;
317
+ export async function runNewCheck(args) {
318
+ if (args.includes('--help') || args.includes('-h')) {
319
+ console.log(NEW_CHECK_HELP);
320
+ process.exit(0);
321
+ }
322
+ const flags = parseFlags(args);
323
+ // --config-dir is required (CLI flag > AGHAST_CONFIG_DIR env var)
324
+ const rawConfigDir = flags.configDir || process.env.AGHAST_CONFIG_DIR;
325
+ if (!rawConfigDir) {
326
+ console.error(formatError(ERROR_CODES.E2001, '--config-dir is required (or set AGHAST_CONFIG_DIR).'));
327
+ process.exit(1);
328
+ }
329
+ const configDir = resolve(rawConfigDir);
330
+ const checksDir = resolve(configDir, 'checks');
331
+ const registryPath = resolve(configDir, 'checks-config.json');
332
+ const inputs = await promptForMissing(flags);
333
+ // Ensure aghast- prefix
334
+ inputs.id = ensurePrefix(inputs.id);
335
+ // Bootstrap: create checks-config.json if it doesn't exist
336
+ let registry;
337
+ if (await checkFileExists(registryPath)) {
338
+ registry = await loadExistingRegistry(registryPath);
339
+ }
340
+ else {
341
+ await mkdir(configDir, { recursive: true });
342
+ const emptyRegistry = { checks: [] };
343
+ await writeFile(registryPath, JSON.stringify(emptyRegistry, null, 2) + '\n', 'utf-8');
344
+ console.log(`Created new config: ${registryPath}`);
345
+ registry = emptyRegistry;
346
+ }
347
+ const validationErrors = validateInputs(inputs, registry);
348
+ if (validationErrors.length > 0) {
349
+ for (const err of validationErrors) {
350
+ console.error(formatError(ERROR_CODES.E2004, err));
351
+ }
352
+ process.exit(1);
353
+ }
354
+ // Create check folder
355
+ const checkFolder = resolve(checksDir, inputs.id);
356
+ if (await checkFileExists(checkFolder)) {
357
+ console.error(formatError(ERROR_CODES.E2004, `Check folder already exists: ${checkFolder}`));
358
+ process.exit(1);
359
+ }
360
+ await mkdir(checkFolder, { recursive: true });
361
+ // Generate and write check.json (Layer 2)
362
+ const checkDef = generateCheckDefinition(inputs);
363
+ await writeFile(resolve(checkFolder, `${inputs.id}.json`), JSON.stringify(checkDef, null, 2) + '\n', 'utf-8');
364
+ console.log(`Created: ${checkFolder}/${inputs.id}.json`);
365
+ // Generate and write instructions.md (skipped for semgrep-only)
366
+ if (inputs.checkType !== 'semgrep-only') {
367
+ const markdown = generateMarkdown(inputs);
368
+ await writeFile(resolve(checkFolder, `${inputs.id}.md`), markdown, 'utf-8');
369
+ console.log(`Created: ${checkFolder}/${inputs.id}.md`);
370
+ }
371
+ // Generate Semgrep rule template and test file if needed
372
+ if ((inputs.checkType === 'semgrep' || inputs.checkType === 'semgrep-only') && !inputs.semgrepRules) {
373
+ const rulePath = resolve(checkFolder, `${inputs.id}.yaml`);
374
+ await writeFile(rulePath, generateSemgrepRule(inputs.id, inputs.language), 'utf-8');
375
+ console.log(`Created: ${checkFolder}/${inputs.id}.yaml (template — edit before running)`);
376
+ // Create corresponding test file
377
+ const langInfo = SUPPORTED_LANGUAGES[inputs.language];
378
+ if (langInfo) {
379
+ const testsDir = resolve(checkFolder, 'tests');
380
+ await mkdir(testsDir, { recursive: true });
381
+ const testFileName = `${inputs.id}${langInfo.extension}`;
382
+ const testPath = resolve(testsDir, testFileName);
383
+ await writeFile(testPath, generateSemgrepTestFile(inputs.id, inputs.language), 'utf-8');
384
+ console.log(`Created: ${checkFolder}/tests/${testFileName} (test scaffold — edit before running)`);
385
+ }
386
+ }
387
+ // Update registry (Layer 1)
388
+ const registryEntry = generateRegistryEntry(inputs);
389
+ registry.checks.push(registryEntry);
390
+ await writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n', 'utf-8');
391
+ console.log(`Updated: ${registryPath}`);
392
+ console.log(`\nNew check "${inputs.id}" created successfully.`);
393
+ }
394
+ // Auto-run when executed directly (pnpm new-check / tsx src/new-check.ts), but not when imported by cli.ts.
395
+ if (!process.env._AGHAST_CLI) {
396
+ await import('dotenv/config');
397
+ runNewCheck(process.argv.slice(2)).catch((err) => {
398
+ const require = createRequire(import.meta.url);
399
+ const pkg = require('../package.json');
400
+ console.error('');
401
+ console.error(formatFatalError(err instanceof Error ? err.message : String(err), pkg.version));
402
+ process.exit(1);
403
+ });
404
+ }
405
+ //# sourceMappingURL=new-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new-check.js","sourceRoot":"","sources":["../src/new-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Generic prompt template prepended to all check executions.
3
+ * Based on SPECIFICATION.md Appendix C.1.
4
+ */
5
+ /**
6
+ * Build the full prompt by prepending generic instructions to check-specific markdown.
7
+ * @param checkInstructions - The check-specific markdown content.
8
+ * @param configDir - Optional config directory containing prompts/ subdirectory.
9
+ * @param genericPrompt - Optional generic prompt template filename (default: 'generic-instructions.md').
10
+ */
11
+ export declare function buildPrompt(checkInstructions: string, configDir?: string, genericPrompt?: string): Promise<string>;
12
+ //# sourceMappingURL=prompt-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-template.d.ts","sourceRoot":"","sources":["../src/prompt-template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2BH;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,iBAAiB,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxH"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Generic prompt template prepended to all check executions.
3
+ * Based on SPECIFICATION.md Appendix C.1.
4
+ */
5
+ import { readFile } from 'node:fs/promises';
6
+ import { existsSync } from 'node:fs';
7
+ import { resolve, dirname } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const DEFAULT_PROMPTS_DIR = resolve(__dirname, '..', 'config', 'prompts');
11
+ function getGenericPromptPath(configDir, genericPrompt) {
12
+ const filename = genericPrompt ?? 'generic-instructions.md';
13
+ if (filename.includes('/') || filename.includes('\\') || filename.includes('..')) {
14
+ throw new Error(`Invalid generic prompt filename: must not contain path separators or "..". Got: "${filename}"`);
15
+ }
16
+ // Try config-dir prompts first, fall back to built-in prompts
17
+ if (configDir) {
18
+ const configPromptPath = resolve(configDir, 'prompts', filename);
19
+ if (existsSync(configPromptPath)) {
20
+ return configPromptPath;
21
+ }
22
+ }
23
+ return resolve(DEFAULT_PROMPTS_DIR, filename);
24
+ }
25
+ /**
26
+ * Build the full prompt by prepending generic instructions to check-specific markdown.
27
+ * @param checkInstructions - The check-specific markdown content.
28
+ * @param configDir - Optional config directory containing prompts/ subdirectory.
29
+ * @param genericPrompt - Optional generic prompt template filename (default: 'generic-instructions.md').
30
+ */
31
+ export async function buildPrompt(checkInstructions, configDir, genericPrompt) {
32
+ const genericPromptContent = await readFile(getGenericPromptPath(configDir, genericPrompt), 'utf-8');
33
+ return genericPromptContent + checkInstructions;
34
+ }
35
+ //# sourceMappingURL=prompt-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-template.js","sourceRoot":"","sources":["../src/prompt-template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,mBAAmB,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAE1E,SAAS,oBAAoB,CAAC,SAAkB,EAAE,aAAsB;IACtE,MAAM,QAAQ,GAAG,aAAa,IAAI,yBAAyB,CAAC;IAC5D,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CACb,oFAAoF,QAAQ,GAAG,CAChG,CAAC;IACJ,CAAC;IACD,8DAA8D;IAC9D,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACjE,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACjC,OAAO,gBAAgB,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,iBAAyB,EAAE,SAAkB,EAAE,aAAsB;IACrG,MAAM,oBAAoB,GAAG,MAAM,QAAQ,CAAC,oBAAoB,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC,CAAC;IACrG,OAAO,oBAAoB,GAAG,iBAAiB,CAAC;AAClD,CAAC"}