@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.
- package/LICENSE +661 -0
- package/README.md +111 -0
- package/config/prompts/generic-instructions.md +56 -0
- package/config/prompts/test-cheaper-instructions.md +57 -0
- package/dist/check-library.d.ts +87 -0
- package/dist/check-library.d.ts.map +1 -0
- package/dist/check-library.js +374 -0
- package/dist/check-library.js.map +1 -0
- package/dist/claude-code-provider.d.ts +26 -0
- package/dist/claude-code-provider.d.ts.map +1 -0
- package/dist/claude-code-provider.js +247 -0
- package/dist/claude-code-provider.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +78 -0
- package/dist/cli.js.map +1 -0
- package/dist/colors.d.ts +7 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +18 -0
- package/dist/colors.js.map +1 -0
- package/dist/error-codes.d.ts +42 -0
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/error-codes.js +60 -0
- package/dist/error-codes.js.map +1 -0
- package/dist/formatters/index.d.ts +10 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +23 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/json-formatter.d.ts +11 -0
- package/dist/formatters/json-formatter.d.ts.map +1 -0
- package/dist/formatters/json-formatter.js +11 -0
- package/dist/formatters/json-formatter.js.map +1 -0
- package/dist/formatters/sarif-formatter.d.ts +18 -0
- package/dist/formatters/sarif-formatter.d.ts.map +1 -0
- package/dist/formatters/sarif-formatter.js +103 -0
- package/dist/formatters/sarif-formatter.js.map +1 -0
- package/dist/formatters/types.d.ts +11 -0
- package/dist/formatters/types.d.ts.map +1 -0
- package/dist/formatters/types.js +6 -0
- package/dist/formatters/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +406 -0
- package/dist/index.js.map +1 -0
- package/dist/logging.d.ts +26 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +79 -0
- package/dist/logging.js.map +1 -0
- package/dist/mock-ai-provider.d.ts +18 -0
- package/dist/mock-ai-provider.d.ts.map +1 -0
- package/dist/mock-ai-provider.js +28 -0
- package/dist/mock-ai-provider.js.map +1 -0
- package/dist/new-check.d.ts +13 -0
- package/dist/new-check.d.ts.map +1 -0
- package/dist/new-check.js +405 -0
- package/dist/new-check.js.map +1 -0
- package/dist/prompt-template.d.ts +12 -0
- package/dist/prompt-template.d.ts.map +1 -0
- package/dist/prompt-template.js +35 -0
- package/dist/prompt-template.js.map +1 -0
- package/dist/provider-registry.d.ts +15 -0
- package/dist/provider-registry.d.ts.map +1 -0
- package/dist/provider-registry.js +27 -0
- package/dist/provider-registry.js.map +1 -0
- package/dist/repository-analyzer.d.ts +68 -0
- package/dist/repository-analyzer.d.ts.map +1 -0
- package/dist/repository-analyzer.js +230 -0
- package/dist/repository-analyzer.js.map +1 -0
- package/dist/response-parser.d.ts +12 -0
- package/dist/response-parser.d.ts.map +1 -0
- package/dist/response-parser.js +109 -0
- package/dist/response-parser.js.map +1 -0
- package/dist/runtime-config.d.ts +15 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +73 -0
- package/dist/runtime-config.js.map +1 -0
- package/dist/sarif-parser.d.ts +20 -0
- package/dist/sarif-parser.d.ts.map +1 -0
- package/dist/sarif-parser.js +76 -0
- package/dist/sarif-parser.js.map +1 -0
- package/dist/scan-runner.d.ts +29 -0
- package/dist/scan-runner.d.ts.map +1 -0
- package/dist/scan-runner.js +559 -0
- package/dist/scan-runner.js.map +1 -0
- package/dist/semgrep-runner.d.ts +25 -0
- package/dist/semgrep-runner.d.ts.map +1 -0
- package/dist/semgrep-runner.js +100 -0
- package/dist/semgrep-runner.js.map +1 -0
- package/dist/snippet-extractor.d.ts +25 -0
- package/dist/snippet-extractor.d.ts.map +1 -0
- package/dist/snippet-extractor.js +56 -0
- package/dist/snippet-extractor.js.map +1 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
package/dist/logging.js
ADDED
|
@@ -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"}
|