codeprobe 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 +21 -0
- package/README.md +406 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +104 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ab.d.ts +7 -0
- package/dist/commands/ab.d.ts.map +1 -0
- package/dist/commands/ab.js +230 -0
- package/dist/commands/ab.js.map +1 -0
- package/dist/commands/agents.d.ts +10 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +326 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/autotest.d.ts +10 -0
- package/dist/commands/autotest.d.ts.map +1 -0
- package/dist/commands/autotest.js +408 -0
- package/dist/commands/autotest.js.map +1 -0
- package/dist/commands/benchmark.d.ts +6 -0
- package/dist/commands/benchmark.d.ts.map +1 -0
- package/dist/commands/benchmark.js +215 -0
- package/dist/commands/benchmark.js.map +1 -0
- package/dist/commands/check.d.ts +10 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +333 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/context.d.ts +16 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +219 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/cost.d.ts +9 -0
- package/dist/commands/cost.d.ts.map +1 -0
- package/dist/commands/cost.js +142 -0
- package/dist/commands/cost.js.map +1 -0
- package/dist/commands/dashboard.d.ts +10 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +462 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/diff.d.ts +6 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +118 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +203 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/explain.d.ts +7 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +164 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/flaky.d.ts +10 -0
- package/dist/commands/flaky.d.ts.map +1 -0
- package/dist/commands/flaky.js +141 -0
- package/dist/commands/flaky.js.map +1 -0
- package/dist/commands/generateClaudeMd.d.ts +11 -0
- package/dist/commands/generateClaudeMd.d.ts.map +1 -0
- package/dist/commands/generateClaudeMd.js +278 -0
- package/dist/commands/generateClaudeMd.js.map +1 -0
- package/dist/commands/generateRules.d.ts +11 -0
- package/dist/commands/generateRules.d.ts.map +1 -0
- package/dist/commands/generateRules.js +413 -0
- package/dist/commands/generateRules.js.map +1 -0
- package/dist/commands/heatmap.d.ts +7 -0
- package/dist/commands/heatmap.d.ts.map +1 -0
- package/dist/commands/heatmap.js +117 -0
- package/dist/commands/heatmap.js.map +1 -0
- package/dist/commands/history.d.ts +13 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +113 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +199 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/improve.d.ts +7 -0
- package/dist/commands/improve.d.ts.map +1 -0
- package/dist/commands/improve.js +192 -0
- package/dist/commands/improve.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +270 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/installHook.d.ts +9 -0
- package/dist/commands/installHook.d.ts.map +1 -0
- package/dist/commands/installHook.js +78 -0
- package/dist/commands/installHook.js.map +1 -0
- package/dist/commands/lint.d.ts +6 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +237 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/map.d.ts +9 -0
- package/dist/commands/map.d.ts.map +1 -0
- package/dist/commands/map.js +114 -0
- package/dist/commands/map.js.map +1 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +151 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/models.d.ts +9 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/models.js +89 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/pack.d.ts +10 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +248 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/recommend.d.ts +10 -0
- package/dist/commands/recommend.d.ts.map +1 -0
- package/dist/commands/recommend.js +472 -0
- package/dist/commands/recommend.js.map +1 -0
- package/dist/commands/regression.d.ts +10 -0
- package/dist/commands/regression.d.ts.map +1 -0
- package/dist/commands/regression.js +212 -0
- package/dist/commands/regression.js.map +1 -0
- package/dist/commands/repl.d.ts +9 -0
- package/dist/commands/repl.d.ts.map +1 -0
- package/dist/commands/repl.js +245 -0
- package/dist/commands/repl.js.map +1 -0
- package/dist/commands/scan.d.ts +10 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +352 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/score.d.ts +10 -0
- package/dist/commands/score.d.ts.map +1 -0
- package/dist/commands/score.js +192 -0
- package/dist/commands/score.js.map +1 -0
- package/dist/commands/security.d.ts +10 -0
- package/dist/commands/security.d.ts.map +1 -0
- package/dist/commands/security.js +211 -0
- package/dist/commands/security.js.map +1 -0
- package/dist/commands/simulate.d.ts +7 -0
- package/dist/commands/simulate.d.ts.map +1 -0
- package/dist/commands/simulate.js +149 -0
- package/dist/commands/simulate.js.map +1 -0
- package/dist/commands/summary.d.ts +9 -0
- package/dist/commands/summary.d.ts.map +1 -0
- package/dist/commands/summary.js +271 -0
- package/dist/commands/summary.js.map +1 -0
- package/dist/commands/test.d.ts +9 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +219 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/ui.d.ts +8 -0
- package/dist/commands/ui.d.ts.map +1 -0
- package/dist/commands/ui.js +222 -0
- package/dist/commands/ui.js.map +1 -0
- package/dist/commands/validate.d.ts +7 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +254 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/workflow.d.ts +39 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +309 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/core/__tests__/contextAnalyzer.test.d.ts +2 -0
- package/dist/core/__tests__/contextAnalyzer.test.d.ts.map +1 -0
- package/dist/core/__tests__/contextAnalyzer.test.js +48 -0
- package/dist/core/__tests__/contextAnalyzer.test.js.map +1 -0
- package/dist/core/__tests__/promptLinter.test.d.ts +2 -0
- package/dist/core/__tests__/promptLinter.test.d.ts.map +1 -0
- package/dist/core/__tests__/promptLinter.test.js +74 -0
- package/dist/core/__tests__/promptLinter.test.js.map +1 -0
- package/dist/core/__tests__/promptRunner.test.d.ts +2 -0
- package/dist/core/__tests__/promptRunner.test.d.ts.map +1 -0
- package/dist/core/__tests__/promptRunner.test.js +84 -0
- package/dist/core/__tests__/promptRunner.test.js.map +1 -0
- package/dist/core/__tests__/securityScanner.test.d.ts +2 -0
- package/dist/core/__tests__/securityScanner.test.d.ts.map +1 -0
- package/dist/core/__tests__/securityScanner.test.js +39 -0
- package/dist/core/__tests__/securityScanner.test.js.map +1 -0
- package/dist/core/agentTracer.d.ts +21 -0
- package/dist/core/agentTracer.d.ts.map +1 -0
- package/dist/core/agentTracer.js +355 -0
- package/dist/core/agentTracer.js.map +1 -0
- package/dist/core/anthropicClient.d.ts +26 -0
- package/dist/core/anthropicClient.d.ts.map +1 -0
- package/dist/core/anthropicClient.js +62 -0
- package/dist/core/anthropicClient.js.map +1 -0
- package/dist/core/benchmarkRunner.d.ts +25 -0
- package/dist/core/benchmarkRunner.d.ts.map +1 -0
- package/dist/core/benchmarkRunner.js +182 -0
- package/dist/core/benchmarkRunner.js.map +1 -0
- package/dist/core/contextAnalyzer.d.ts +19 -0
- package/dist/core/contextAnalyzer.d.ts.map +1 -0
- package/dist/core/contextAnalyzer.js +221 -0
- package/dist/core/contextAnalyzer.js.map +1 -0
- package/dist/core/contextPacker.d.ts +26 -0
- package/dist/core/contextPacker.d.ts.map +1 -0
- package/dist/core/contextPacker.js +358 -0
- package/dist/core/contextPacker.js.map +1 -0
- package/dist/core/datasetRunner.d.ts +10 -0
- package/dist/core/datasetRunner.d.ts.map +1 -0
- package/dist/core/datasetRunner.js +130 -0
- package/dist/core/datasetRunner.js.map +1 -0
- package/dist/core/doctorRunner.d.ts +24 -0
- package/dist/core/doctorRunner.d.ts.map +1 -0
- package/dist/core/doctorRunner.js +278 -0
- package/dist/core/doctorRunner.js.map +1 -0
- package/dist/core/hookScanner.d.ts +24 -0
- package/dist/core/hookScanner.d.ts.map +1 -0
- package/dist/core/hookScanner.js +226 -0
- package/dist/core/hookScanner.js.map +1 -0
- package/dist/core/mcpScanner.d.ts +22 -0
- package/dist/core/mcpScanner.d.ts.map +1 -0
- package/dist/core/mcpScanner.js +290 -0
- package/dist/core/mcpScanner.js.map +1 -0
- package/dist/core/modelRegistry.d.ts +35 -0
- package/dist/core/modelRegistry.d.ts.map +1 -0
- package/dist/core/modelRegistry.js +97 -0
- package/dist/core/modelRegistry.js.map +1 -0
- package/dist/core/promptDiff.d.ts +25 -0
- package/dist/core/promptDiff.d.ts.map +1 -0
- package/dist/core/promptDiff.js +130 -0
- package/dist/core/promptDiff.js.map +1 -0
- package/dist/core/promptExplainer.d.ts +17 -0
- package/dist/core/promptExplainer.d.ts.map +1 -0
- package/dist/core/promptExplainer.js +334 -0
- package/dist/core/promptExplainer.js.map +1 -0
- package/dist/core/promptImprover.d.ts +19 -0
- package/dist/core/promptImprover.d.ts.map +1 -0
- package/dist/core/promptImprover.js +260 -0
- package/dist/core/promptImprover.js.map +1 -0
- package/dist/core/promptLinter.d.ts +24 -0
- package/dist/core/promptLinter.d.ts.map +1 -0
- package/dist/core/promptLinter.js +319 -0
- package/dist/core/promptLinter.js.map +1 -0
- package/dist/core/promptRunner.d.ts +31 -0
- package/dist/core/promptRunner.d.ts.map +1 -0
- package/dist/core/promptRunner.js +427 -0
- package/dist/core/promptRunner.js.map +1 -0
- package/dist/core/providers/anthropic.d.ts +10 -0
- package/dist/core/providers/anthropic.d.ts.map +1 -0
- package/dist/core/providers/anthropic.js +26 -0
- package/dist/core/providers/anthropic.js.map +1 -0
- package/dist/core/providers/base.d.ts +22 -0
- package/dist/core/providers/base.d.ts.map +1 -0
- package/dist/core/providers/base.js +2 -0
- package/dist/core/providers/base.js.map +1 -0
- package/dist/core/providers/factory.d.ts +7 -0
- package/dist/core/providers/factory.d.ts.map +1 -0
- package/dist/core/providers/factory.js +42 -0
- package/dist/core/providers/factory.js.map +1 -0
- package/dist/core/providers/google.d.ts +10 -0
- package/dist/core/providers/google.d.ts.map +1 -0
- package/dist/core/providers/google.js +47 -0
- package/dist/core/providers/google.js.map +1 -0
- package/dist/core/providers/openai.d.ts +19 -0
- package/dist/core/providers/openai.d.ts.map +1 -0
- package/dist/core/providers/openai.js +54 -0
- package/dist/core/providers/openai.js.map +1 -0
- package/dist/core/regressionRunner.d.ts +11 -0
- package/dist/core/regressionRunner.d.ts.map +1 -0
- package/dist/core/regressionRunner.js +116 -0
- package/dist/core/regressionRunner.js.map +1 -0
- package/dist/core/repositorySimulator.d.ts +17 -0
- package/dist/core/repositorySimulator.d.ts.map +1 -0
- package/dist/core/repositorySimulator.js +104 -0
- package/dist/core/repositorySimulator.js.map +1 -0
- package/dist/core/scorer.d.ts +30 -0
- package/dist/core/scorer.d.ts.map +1 -0
- package/dist/core/scorer.js +317 -0
- package/dist/core/scorer.js.map +1 -0
- package/dist/core/securityScanner.d.ts +23 -0
- package/dist/core/securityScanner.d.ts.map +1 -0
- package/dist/core/securityScanner.js +216 -0
- package/dist/core/securityScanner.js.map +1 -0
- package/dist/core/skillValidator.d.ts +41 -0
- package/dist/core/skillValidator.d.ts.map +1 -0
- package/dist/core/skillValidator.js +235 -0
- package/dist/core/skillValidator.js.map +1 -0
- package/dist/core/testHistory.d.ts +44 -0
- package/dist/core/testHistory.d.ts.map +1 -0
- package/dist/core/testHistory.js +91 -0
- package/dist/core/testHistory.js.map +1 -0
- package/dist/tokenizers/claudeTokenizer.d.ts +26 -0
- package/dist/tokenizers/claudeTokenizer.d.ts.map +1 -0
- package/dist/tokenizers/claudeTokenizer.js +83 -0
- package/dist/tokenizers/claudeTokenizer.js.map +1 -0
- package/dist/types/agent.d.ts +26 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +5 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/config.d.ts +30 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +5 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/context.d.ts +77 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +5 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/dataset.d.ts +26 -0
- package/dist/types/dataset.d.ts.map +1 -0
- package/dist/types/dataset.js +5 -0
- package/dist/types/dataset.js.map +1 -0
- package/dist/types/diagnostics.d.ts +31 -0
- package/dist/types/diagnostics.d.ts.map +1 -0
- package/dist/types/diagnostics.js +5 -0
- package/dist/types/diagnostics.js.map +1 -0
- package/dist/types/prompt.d.ts +53 -0
- package/dist/types/prompt.d.ts.map +1 -0
- package/dist/types/prompt.js +5 -0
- package/dist/types/prompt.js.map +1 -0
- package/dist/types/results.d.ts +42 -0
- package/dist/types/results.d.ts.map +1 -0
- package/dist/types/results.js +5 -0
- package/dist/types/results.js.map +1 -0
- package/dist/ui/dashboard.d.ts +57 -0
- package/dist/ui/dashboard.d.ts.map +1 -0
- package/dist/ui/dashboard.js +644 -0
- package/dist/ui/dashboard.js.map +1 -0
- package/dist/utils/__tests__/hashing.test.d.ts +2 -0
- package/dist/utils/__tests__/hashing.test.d.ts.map +1 -0
- package/dist/utils/__tests__/hashing.test.js +28 -0
- package/dist/utils/__tests__/hashing.test.js.map +1 -0
- package/dist/utils/__tests__/output.test.d.ts +2 -0
- package/dist/utils/__tests__/output.test.d.ts.map +1 -0
- package/dist/utils/__tests__/output.test.js +62 -0
- package/dist/utils/__tests__/output.test.js.map +1 -0
- package/dist/utils/cache.d.ts +29 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +87 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +15 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +61 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/errors.d.ts +43 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +83 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/fs.d.ts +44 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +119 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/hashing.d.ts +13 -0
- package/dist/utils/hashing.d.ts.map +1 -0
- package/dist/utils/hashing.js +18 -0
- package/dist/utils/hashing.js.map +1 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +76 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/output.d.ts +34 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +99 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/paths.d.ts +33 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +51 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/spinner.d.ts +23 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +79 -0
- package/dist/utils/spinner.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoring engine that evaluates prompt outputs on multiple criteria
|
|
3
|
+
* and produces a 0-100 score with an A-F grade.
|
|
4
|
+
*
|
|
5
|
+
* All scoring is heuristic — no API calls are made.
|
|
6
|
+
*/
|
|
7
|
+
import { evaluateAssertions } from './promptRunner.js';
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Grade mapping
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
function gradeFromScore(score) {
|
|
12
|
+
if (score >= 90)
|
|
13
|
+
return 'A';
|
|
14
|
+
if (score >= 80)
|
|
15
|
+
return 'B';
|
|
16
|
+
if (score >= 70)
|
|
17
|
+
return 'C';
|
|
18
|
+
if (score >= 60)
|
|
19
|
+
return 'D';
|
|
20
|
+
return 'F';
|
|
21
|
+
}
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Criterion 1: Assertion pass rate (weight 0.4)
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
function scoreAssertions(output, test) {
|
|
26
|
+
const expect = test.expect;
|
|
27
|
+
if (!expect) {
|
|
28
|
+
return {
|
|
29
|
+
name: 'Assertions',
|
|
30
|
+
score: 100,
|
|
31
|
+
weight: 0.4,
|
|
32
|
+
details: 'No assertions defined — full marks by default',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const results = evaluateAssertions(output, expect);
|
|
36
|
+
if (results.length === 0) {
|
|
37
|
+
return {
|
|
38
|
+
name: 'Assertions',
|
|
39
|
+
score: 100,
|
|
40
|
+
weight: 0.4,
|
|
41
|
+
details: 'No assertions evaluated — full marks by default',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const passed = results.filter((r) => r.passed).length;
|
|
45
|
+
const total = results.length;
|
|
46
|
+
const rate = passed / total;
|
|
47
|
+
const score = Math.round(rate * 100);
|
|
48
|
+
return {
|
|
49
|
+
name: 'Assertions',
|
|
50
|
+
score,
|
|
51
|
+
weight: 0.4,
|
|
52
|
+
details: `${passed}/${total} assertions passed (${score}%)`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Criterion 2: Length appropriateness (weight 0.15)
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
function scoreLengthAppropriateness(output, spec, test) {
|
|
59
|
+
const inputText = test.input ?? spec.prompt;
|
|
60
|
+
const inputLength = inputText.length;
|
|
61
|
+
const outputLength = output.length;
|
|
62
|
+
// Heuristics:
|
|
63
|
+
// - Output should not be empty
|
|
64
|
+
// - Output should not be excessively short (< 10 chars) or excessively long (> 20x input)
|
|
65
|
+
// - A reasonable ratio is between 0.1x and 5x the input length
|
|
66
|
+
if (outputLength === 0) {
|
|
67
|
+
return {
|
|
68
|
+
name: 'Length',
|
|
69
|
+
score: 0,
|
|
70
|
+
weight: 0.15,
|
|
71
|
+
details: 'Output is empty',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Check explicit length constraints from the test expectations
|
|
75
|
+
const expect = test.expect;
|
|
76
|
+
let penaltyFromConstraints = 0;
|
|
77
|
+
if (expect?.minLength !== undefined && outputLength < expect.minLength) {
|
|
78
|
+
penaltyFromConstraints += 40;
|
|
79
|
+
}
|
|
80
|
+
if (expect?.maxLength !== undefined && outputLength > expect.maxLength) {
|
|
81
|
+
penaltyFromConstraints += 40;
|
|
82
|
+
}
|
|
83
|
+
// General heuristic: output ratio
|
|
84
|
+
const ratio = outputLength / Math.max(inputLength, 1);
|
|
85
|
+
let heuristicScore = 100;
|
|
86
|
+
if (outputLength < 10) {
|
|
87
|
+
heuristicScore = 20;
|
|
88
|
+
}
|
|
89
|
+
else if (ratio < 0.05) {
|
|
90
|
+
// Very short compared to input
|
|
91
|
+
heuristicScore = 50;
|
|
92
|
+
}
|
|
93
|
+
else if (ratio > 20) {
|
|
94
|
+
// Excessively long compared to input
|
|
95
|
+
heuristicScore = 60;
|
|
96
|
+
}
|
|
97
|
+
else if (ratio >= 0.1 && ratio <= 5) {
|
|
98
|
+
// Sweet spot
|
|
99
|
+
heuristicScore = 100;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Acceptable but not ideal
|
|
103
|
+
heuristicScore = 80;
|
|
104
|
+
}
|
|
105
|
+
const score = Math.max(0, Math.min(100, heuristicScore - penaltyFromConstraints));
|
|
106
|
+
return {
|
|
107
|
+
name: 'Length',
|
|
108
|
+
score,
|
|
109
|
+
weight: 0.15,
|
|
110
|
+
details: `Output: ${outputLength} chars, Input: ${inputLength} chars (ratio: ${ratio.toFixed(2)})`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Criterion 3: Format compliance (weight 0.15)
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
function scoreFormatCompliance(output, spec) {
|
|
117
|
+
const combined = [spec.prompt, spec.system ?? ''].join(' ').toLowerCase();
|
|
118
|
+
const checks = [];
|
|
119
|
+
// Check for bullet point format hints
|
|
120
|
+
const wantsBullets = combined.includes('bullet') ||
|
|
121
|
+
combined.includes('- ') ||
|
|
122
|
+
/\blist\b/.test(combined) ||
|
|
123
|
+
/\bpoints?\b/.test(combined);
|
|
124
|
+
if (wantsBullets) {
|
|
125
|
+
const hasBullets = /^[\s]*[-*]\s/m.test(output);
|
|
126
|
+
checks.push({ hint: 'bullet points', detected: true, present: hasBullets });
|
|
127
|
+
}
|
|
128
|
+
// Check for JSON format hints
|
|
129
|
+
const wantsJson = combined.includes('json') || combined.includes('object');
|
|
130
|
+
if (wantsJson) {
|
|
131
|
+
let isValidJson = false;
|
|
132
|
+
try {
|
|
133
|
+
JSON.parse(output.trim());
|
|
134
|
+
isValidJson = true;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Not valid JSON
|
|
138
|
+
}
|
|
139
|
+
checks.push({ hint: 'JSON format', detected: true, present: isValidJson });
|
|
140
|
+
}
|
|
141
|
+
// Check for numbered list format hints
|
|
142
|
+
const wantsNumbered = combined.includes('numbered') || combined.includes('step');
|
|
143
|
+
if (wantsNumbered) {
|
|
144
|
+
const hasNumbers = /^\s*\d+[.)]\s/m.test(output);
|
|
145
|
+
checks.push({ hint: 'numbered list', detected: true, present: hasNumbers });
|
|
146
|
+
}
|
|
147
|
+
// Check for heading/section hints
|
|
148
|
+
const wantsHeadings = combined.includes('heading') || combined.includes('section');
|
|
149
|
+
if (wantsHeadings) {
|
|
150
|
+
const hasHeadings = /^#+\s/m.test(output) || /^[A-Z][^.!?]*:$/m.test(output);
|
|
151
|
+
checks.push({ hint: 'headings', detected: true, present: hasHeadings });
|
|
152
|
+
}
|
|
153
|
+
if (checks.length === 0) {
|
|
154
|
+
return {
|
|
155
|
+
name: 'Format',
|
|
156
|
+
score: 80,
|
|
157
|
+
weight: 0.15,
|
|
158
|
+
details: 'No specific format hints detected in prompt — default score',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const matched = checks.filter((c) => c.present).length;
|
|
162
|
+
const score = Math.round((matched / checks.length) * 100);
|
|
163
|
+
const matchedHints = checks
|
|
164
|
+
.map((c) => `${c.hint}: ${c.present ? 'yes' : 'no'}`)
|
|
165
|
+
.join(', ');
|
|
166
|
+
return {
|
|
167
|
+
name: 'Format',
|
|
168
|
+
score,
|
|
169
|
+
weight: 0.15,
|
|
170
|
+
details: `Format checks: ${matchedHints}`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Criterion 4: Relevance — word overlap (weight 0.15)
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
function scoreRelevance(output, spec, test) {
|
|
177
|
+
const inputText = test.input ?? spec.prompt;
|
|
178
|
+
// Extract meaningful words from the input (>= 3 chars, lowercase, no stopwords)
|
|
179
|
+
const stopwords = new Set([
|
|
180
|
+
'the', 'and', 'for', 'that', 'this', 'with', 'are', 'was', 'were', 'been',
|
|
181
|
+
'has', 'have', 'had', 'not', 'but', 'can', 'will', 'from', 'they', 'them',
|
|
182
|
+
'its', 'you', 'your', 'all', 'about', 'into', 'also', 'more', 'some',
|
|
183
|
+
'than', 'each', 'which', 'their', 'would', 'could', 'should', 'there',
|
|
184
|
+
]);
|
|
185
|
+
const extractWords = (text) => {
|
|
186
|
+
const words = text.toLowerCase()
|
|
187
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
188
|
+
.split(/\s+/)
|
|
189
|
+
.filter((w) => w.length >= 3 && !stopwords.has(w));
|
|
190
|
+
return new Set(words);
|
|
191
|
+
};
|
|
192
|
+
const inputWords = extractWords(inputText);
|
|
193
|
+
const outputWords = extractWords(output);
|
|
194
|
+
if (inputWords.size === 0) {
|
|
195
|
+
return {
|
|
196
|
+
name: 'Relevance',
|
|
197
|
+
score: 80,
|
|
198
|
+
weight: 0.15,
|
|
199
|
+
details: 'No meaningful input words to compare — default score',
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// Calculate overlap: what fraction of input keywords appear in the output?
|
|
203
|
+
let overlap = 0;
|
|
204
|
+
for (const word of inputWords) {
|
|
205
|
+
if (outputWords.has(word)) {
|
|
206
|
+
overlap++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const overlapRate = overlap / inputWords.size;
|
|
210
|
+
// Score: scale 0-1 overlap to 0-100, with a floor at 20 for non-empty output
|
|
211
|
+
let score;
|
|
212
|
+
if (output.trim().length === 0) {
|
|
213
|
+
score = 0;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// Use a generous curve: 30% overlap is already quite good for a summary
|
|
217
|
+
score = Math.min(100, Math.round(overlapRate * 200));
|
|
218
|
+
score = Math.max(score, 20); // At least 20 for producing any response
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
name: 'Relevance',
|
|
222
|
+
score,
|
|
223
|
+
weight: 0.15,
|
|
224
|
+
details: `${overlap}/${inputWords.size} input keywords found in output (${Math.round(overlapRate * 100)}% overlap)`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Criterion 5: Completeness (weight 0.15)
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
function scoreCompleteness(output) {
|
|
231
|
+
const trimmed = output.trim();
|
|
232
|
+
if (trimmed.length === 0) {
|
|
233
|
+
return {
|
|
234
|
+
name: 'Completeness',
|
|
235
|
+
score: 0,
|
|
236
|
+
weight: 0.15,
|
|
237
|
+
details: 'Output is empty',
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
let score = 100;
|
|
241
|
+
const issues = [];
|
|
242
|
+
// Check 1: Does it end mid-sentence? (ends with common incomplete indicators)
|
|
243
|
+
const lastChar = trimmed[trimmed.length - 1];
|
|
244
|
+
const sentenceEnders = new Set(['.', '!', '?', ':', ';', ')', ']', '}', '"', "'", '`']);
|
|
245
|
+
const bulletEnder = /[-*]\s*$/;
|
|
246
|
+
if (!sentenceEnders.has(lastChar) && !bulletEnder.test(trimmed)) {
|
|
247
|
+
// Might be mid-sentence — penalize unless it's a list item
|
|
248
|
+
const lastLine = trimmed.split('\n').pop() ?? '';
|
|
249
|
+
const isListItem = /^[\s]*[-*\d.]+\s/.test(lastLine);
|
|
250
|
+
if (!isListItem) {
|
|
251
|
+
score -= 20;
|
|
252
|
+
issues.push('may end mid-sentence');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Check 2: Very short output (< 20 chars) is likely incomplete
|
|
256
|
+
if (trimmed.length < 20) {
|
|
257
|
+
score -= 15;
|
|
258
|
+
issues.push('very short output');
|
|
259
|
+
}
|
|
260
|
+
// Check 3: Unbalanced brackets/quotes suggest truncation
|
|
261
|
+
const brackets = [['(', ')'], ['[', ']'], ['{', '}']];
|
|
262
|
+
for (const [open, close] of brackets) {
|
|
263
|
+
const openCount = (trimmed.match(new RegExp(`\\${open}`, 'g')) ?? []).length;
|
|
264
|
+
const closeCount = (trimmed.match(new RegExp(`\\${close}`, 'g')) ?? []).length;
|
|
265
|
+
if (openCount > closeCount) {
|
|
266
|
+
score -= 10;
|
|
267
|
+
issues.push(`unbalanced ${open}${close}`);
|
|
268
|
+
break; // Only penalize once for bracket issues
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Check 4: Ends with ellipsis or continuation markers
|
|
272
|
+
if (trimmed.endsWith('...') || trimmed.endsWith('etc') || trimmed.endsWith('and so on')) {
|
|
273
|
+
score -= 5;
|
|
274
|
+
issues.push('ends with continuation marker');
|
|
275
|
+
}
|
|
276
|
+
score = Math.max(0, score);
|
|
277
|
+
return {
|
|
278
|
+
name: 'Completeness',
|
|
279
|
+
score,
|
|
280
|
+
weight: 0.15,
|
|
281
|
+
details: issues.length > 0
|
|
282
|
+
? `Issues: ${issues.join(', ')}`
|
|
283
|
+
: 'Output appears complete and well-formed',
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// Main scoring function
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
/**
|
|
290
|
+
* Score a prompt output on multiple criteria and produce a 0-100 score.
|
|
291
|
+
*
|
|
292
|
+
* Criteria and weights:
|
|
293
|
+
* 1. Assertion pass rate (0.40)
|
|
294
|
+
* 2. Length appropriateness (0.15)
|
|
295
|
+
* 3. Format compliance (0.15)
|
|
296
|
+
* 4. Relevance (0.15)
|
|
297
|
+
* 5. Completeness (0.15)
|
|
298
|
+
*/
|
|
299
|
+
export function scoreOutput(output, spec, test) {
|
|
300
|
+
const criteria = [
|
|
301
|
+
scoreAssertions(output, test),
|
|
302
|
+
scoreLengthAppropriateness(output, spec, test),
|
|
303
|
+
scoreFormatCompliance(output, spec),
|
|
304
|
+
scoreRelevance(output, spec, test),
|
|
305
|
+
scoreCompleteness(output),
|
|
306
|
+
];
|
|
307
|
+
// Weighted average
|
|
308
|
+
const weightedSum = criteria.reduce((sum, c) => sum + c.score * c.weight, 0);
|
|
309
|
+
const totalWeight = criteria.reduce((sum, c) => sum + c.weight, 0);
|
|
310
|
+
const overall = Math.round(weightedSum / totalWeight);
|
|
311
|
+
return {
|
|
312
|
+
overall,
|
|
313
|
+
criteria,
|
|
314
|
+
grade: gradeFromScore(overall),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.js","sourceRoot":"","sources":["../../src/core/scorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security scanner for prompt specification files.
|
|
3
|
+
*
|
|
4
|
+
* Checks prompt files for common security vulnerabilities including
|
|
5
|
+
* prompt injection, unsafe interpolation, secret leakage, dangerous
|
|
6
|
+
* permission grants, and instruction override patterns.
|
|
7
|
+
*/
|
|
8
|
+
import type { SecurityFinding } from '../types/diagnostics.js';
|
|
9
|
+
/**
|
|
10
|
+
* Scan a repository's prompt files for security issues.
|
|
11
|
+
*
|
|
12
|
+
* Checks for:
|
|
13
|
+
* 1. `prompt-injection`: Patterns that override or ignore previous instructions
|
|
14
|
+
* 2. `unsafe-interpolation`: Template patterns without sanitization
|
|
15
|
+
* 3. `secret-leakage`: API keys, tokens, passwords embedded in prompts
|
|
16
|
+
* 4. `dangerous-structure`: System prompts granting excessive permissions
|
|
17
|
+
* 5. `instruction-override`: Patterns that replace system instructions
|
|
18
|
+
*
|
|
19
|
+
* @param rootPath Absolute path to the repository root.
|
|
20
|
+
* @returns Array of security findings sorted by severity.
|
|
21
|
+
*/
|
|
22
|
+
export declare function scanSecurity(rootPath: string): Promise<SecurityFinding[]>;
|
|
23
|
+
//# sourceMappingURL=securityScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"securityScanner.d.ts","sourceRoot":"","sources":["../../src/core/securityScanner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AA2L/D;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,EAAE,CAAC,CAwC5B"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security scanner for prompt specification files.
|
|
3
|
+
*
|
|
4
|
+
* Checks prompt files for common security vulnerabilities including
|
|
5
|
+
* prompt injection, unsafe interpolation, secret leakage, dangerous
|
|
6
|
+
* permission grants, and instruction override patterns.
|
|
7
|
+
*/
|
|
8
|
+
import { extname } from 'node:path';
|
|
9
|
+
import { stat } from 'node:fs/promises';
|
|
10
|
+
import { walkDirectory, readTextFile } from '../utils/fs.js';
|
|
11
|
+
/** Directories to skip during traversal. */
|
|
12
|
+
const SKIP_DIRS = new Set([
|
|
13
|
+
'node_modules', '.git', 'dist', 'build', 'coverage',
|
|
14
|
+
'__pycache__', '.next', '.nuxt', '.cache', '.turbo',
|
|
15
|
+
'.parcel-cache', '.vscode', '.idea', 'vendor', 'tmp',
|
|
16
|
+
'.tmp', '.terraform',
|
|
17
|
+
]);
|
|
18
|
+
/** File extensions to scan. */
|
|
19
|
+
const SCANNABLE_EXTENSIONS = new Set([
|
|
20
|
+
'.yaml', '.yml', '.json', '.md', '.txt',
|
|
21
|
+
'.ts', '.js', '.mjs', '.cjs',
|
|
22
|
+
'.prompt', '.template',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* Security rules for prompt injection detection.
|
|
26
|
+
*/
|
|
27
|
+
const INJECTION_RULES = [
|
|
28
|
+
{
|
|
29
|
+
id: 'prompt-injection',
|
|
30
|
+
severity: 'critical',
|
|
31
|
+
patterns: [
|
|
32
|
+
/ignore\s+(all\s+)?previous\s+instructions/i,
|
|
33
|
+
/disregard\s+(all\s+)?(previous|above|prior)\s+(instructions|context|rules)/i,
|
|
34
|
+
/forget\s+(all\s+)?(previous|above|prior)/i,
|
|
35
|
+
/you\s+are\s+now\s+a\s+/i,
|
|
36
|
+
/new\s+role\s*:/i,
|
|
37
|
+
/from\s+now\s+on\s*,?\s+(you|ignore|disregard)/i,
|
|
38
|
+
/system\s*:\s*you\s+are/i,
|
|
39
|
+
],
|
|
40
|
+
message: 'Potential prompt injection pattern detected. This text could be used to override system instructions.',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Security rules for unsafe interpolation detection.
|
|
45
|
+
*/
|
|
46
|
+
const INTERPOLATION_RULES = [
|
|
47
|
+
{
|
|
48
|
+
id: 'unsafe-interpolation',
|
|
49
|
+
severity: 'high',
|
|
50
|
+
patterns: [
|
|
51
|
+
/\$\{[^}]*\}/, // JavaScript template literals
|
|
52
|
+
/\{\{[^}]*\}\}/, // Mustache/Handlebars
|
|
53
|
+
/\{%[^%]*%\}/, // Jinja2
|
|
54
|
+
/<%[^%]*%>/, // ERB
|
|
55
|
+
/\$\{.*user.*\}/i, // User input in template
|
|
56
|
+
/\{\{.*input.*\}\}/i, // Input in template
|
|
57
|
+
],
|
|
58
|
+
message: 'Template interpolation detected without apparent sanitization. User input could be injected into the prompt.',
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
/**
|
|
62
|
+
* Security rules for secret leakage detection.
|
|
63
|
+
*/
|
|
64
|
+
const SECRET_RULES = [
|
|
65
|
+
{
|
|
66
|
+
id: 'secret-leakage',
|
|
67
|
+
severity: 'critical',
|
|
68
|
+
patterns: [
|
|
69
|
+
/(?:api[_-]?key|apikey)\s*[:=]\s*["']?[a-zA-Z0-9_\-]{20,}/i,
|
|
70
|
+
/(?:secret|token|password|passwd|pwd)\s*[:=]\s*["']?[a-zA-Z0-9_\-]{8,}/i,
|
|
71
|
+
/sk-[a-zA-Z0-9]{20,}/, // OpenAI/Anthropic key format
|
|
72
|
+
/ghp_[a-zA-Z0-9]{36}/, // GitHub personal access token
|
|
73
|
+
/gho_[a-zA-Z0-9]{36}/, // GitHub OAuth token
|
|
74
|
+
/Bearer\s+[a-zA-Z0-9_\-\.]{20,}/, // Bearer tokens
|
|
75
|
+
/-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/, // PEM private keys
|
|
76
|
+
/AKIA[0-9A-Z]{16}/, // AWS access key ID
|
|
77
|
+
],
|
|
78
|
+
message: 'Possible secret or credential found in prompt file. Never embed secrets directly in prompts.',
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* Security rules for dangerous permission structures.
|
|
83
|
+
*/
|
|
84
|
+
const DANGEROUS_STRUCTURE_RULES = [
|
|
85
|
+
{
|
|
86
|
+
id: 'dangerous-structure',
|
|
87
|
+
severity: 'high',
|
|
88
|
+
patterns: [
|
|
89
|
+
/you\s+(can|may|have|are\s+allowed\s+to)\s+.*\b(execute|run|eval|delete|drop|rm\s+-rf)\b/i,
|
|
90
|
+
/full\s+access\s+to/i,
|
|
91
|
+
/unrestricted\s+access/i,
|
|
92
|
+
/no\s+restrictions/i,
|
|
93
|
+
/you\s+have\s+root\s+access/i,
|
|
94
|
+
/you\s+can\s+do\s+anything/i,
|
|
95
|
+
/admin(istrator)?\s+privileges/i,
|
|
96
|
+
/bypass\s+(all\s+)?(security|restrictions|limits|filters)/i,
|
|
97
|
+
],
|
|
98
|
+
message: 'System prompt grants excessive permissions. Restrict capabilities to only what is necessary.',
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
/**
|
|
102
|
+
* Security rules for instruction override patterns.
|
|
103
|
+
*/
|
|
104
|
+
const OVERRIDE_RULES = [
|
|
105
|
+
{
|
|
106
|
+
id: 'instruction-override',
|
|
107
|
+
severity: 'high',
|
|
108
|
+
patterns: [
|
|
109
|
+
/override\s+(all\s+)?(system|previous|prior)\s+(instructions|rules|constraints)/i,
|
|
110
|
+
/replace\s+(the\s+)?(system|previous)\s+(prompt|instructions)/i,
|
|
111
|
+
/the\s+following\s+instructions?\s+(supersede|override|replace)/i,
|
|
112
|
+
/above\s+instructions?\s+(are|is)\s+(no\s+longer|void|invalid|overridden)/i,
|
|
113
|
+
/new\s+system\s+prompt/i,
|
|
114
|
+
/updated?\s+instructions?\s*:/i,
|
|
115
|
+
],
|
|
116
|
+
message: 'Pattern that attempts to override or replace system instructions detected.',
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
/** All rule categories. */
|
|
120
|
+
const ALL_RULES = [
|
|
121
|
+
...INJECTION_RULES,
|
|
122
|
+
...INTERPOLATION_RULES,
|
|
123
|
+
...SECRET_RULES,
|
|
124
|
+
...DANGEROUS_STRUCTURE_RULES,
|
|
125
|
+
...OVERRIDE_RULES,
|
|
126
|
+
];
|
|
127
|
+
/**
|
|
128
|
+
* Scan a single file's content against all security rules.
|
|
129
|
+
*/
|
|
130
|
+
function scanContent(filePath, content) {
|
|
131
|
+
const findings = [];
|
|
132
|
+
const lines = content.split('\n');
|
|
133
|
+
for (const rule of ALL_RULES) {
|
|
134
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
135
|
+
const line = lines[lineIndex];
|
|
136
|
+
for (const pattern of rule.patterns) {
|
|
137
|
+
const match = pattern.exec(line);
|
|
138
|
+
if (match) {
|
|
139
|
+
// Avoid duplicate findings for the same rule on the same line
|
|
140
|
+
const alreadyFound = findings.some((f) => f.file === filePath &&
|
|
141
|
+
f.rule === rule.id &&
|
|
142
|
+
f.line === lineIndex + 1);
|
|
143
|
+
if (!alreadyFound) {
|
|
144
|
+
// Create a snippet with some context
|
|
145
|
+
const snippetStart = Math.max(0, match.index - 20);
|
|
146
|
+
const snippetEnd = Math.min(line.length, match.index + match[0].length + 20);
|
|
147
|
+
const snippet = line.slice(snippetStart, snippetEnd).trim();
|
|
148
|
+
findings.push({
|
|
149
|
+
file: filePath,
|
|
150
|
+
rule: rule.id,
|
|
151
|
+
severity: rule.severity,
|
|
152
|
+
message: rule.message,
|
|
153
|
+
line: lineIndex + 1,
|
|
154
|
+
snippet: snippet.length > 100 ? snippet.slice(0, 97) + '...' : snippet,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
break; // Only report first pattern match per rule per line
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return findings;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Scan a repository's prompt files for security issues.
|
|
166
|
+
*
|
|
167
|
+
* Checks for:
|
|
168
|
+
* 1. `prompt-injection`: Patterns that override or ignore previous instructions
|
|
169
|
+
* 2. `unsafe-interpolation`: Template patterns without sanitization
|
|
170
|
+
* 3. `secret-leakage`: API keys, tokens, passwords embedded in prompts
|
|
171
|
+
* 4. `dangerous-structure`: System prompts granting excessive permissions
|
|
172
|
+
* 5. `instruction-override`: Patterns that replace system instructions
|
|
173
|
+
*
|
|
174
|
+
* @param rootPath Absolute path to the repository root.
|
|
175
|
+
* @returns Array of security findings sorted by severity.
|
|
176
|
+
*/
|
|
177
|
+
export async function scanSecurity(rootPath) {
|
|
178
|
+
// Verify root exists
|
|
179
|
+
try {
|
|
180
|
+
const rootStat = await stat(rootPath);
|
|
181
|
+
if (!rootStat.isDirectory())
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
const findings = [];
|
|
188
|
+
const entries = await walkDirectory(rootPath, { ignoreDirs: SKIP_DIRS });
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (!entry.isFile)
|
|
191
|
+
continue;
|
|
192
|
+
const ext = extname(entry.path).toLowerCase();
|
|
193
|
+
if (!SCANNABLE_EXTENSIONS.has(ext))
|
|
194
|
+
continue;
|
|
195
|
+
const content = await readTextFile(entry.path);
|
|
196
|
+
if (!content)
|
|
197
|
+
continue;
|
|
198
|
+
const fileFindings = scanContent(entry.path, content);
|
|
199
|
+
findings.push(...fileFindings);
|
|
200
|
+
}
|
|
201
|
+
// Sort by severity: critical > high > medium > low
|
|
202
|
+
const severityOrder = {
|
|
203
|
+
critical: 0,
|
|
204
|
+
high: 1,
|
|
205
|
+
medium: 2,
|
|
206
|
+
low: 3,
|
|
207
|
+
};
|
|
208
|
+
findings.sort((a, b) => {
|
|
209
|
+
const sevDiff = severityOrder[a.severity] - severityOrder[b.severity];
|
|
210
|
+
if (sevDiff !== 0)
|
|
211
|
+
return sevDiff;
|
|
212
|
+
return a.file.localeCompare(b.file);
|
|
213
|
+
});
|
|
214
|
+
return findings;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=securityScanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"securityScanner.js","sourceRoot":"","sources":["../../src/core/securityScanner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE7D,4CAA4C;AAC5C,MAAM,SAAS,GAAgB,IAAI,GAAG,CAAC;IACrC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;IACnD,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ;IACnD,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK;IACpD,MAAM,EAAE,YAAY;CACrB,CAAC,CAAC;AAEH,+BAA+B;AAC/B,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IACvC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM;IAC5B,SAAS,EAAE,WAAW;CACvB,CAAC,CAAC;AAYH;;GAEG;AACH,MAAM,eAAe,GAAgC;IACnD;QACE,EAAE,EAAE,kBAAkB;QACtB,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE;YACR,4CAA4C;YAC5C,6EAA6E;YAC7E,2CAA2C;YAC3C,yBAAyB;YACzB,iBAAiB;YACjB,gDAAgD;YAChD,yBAAyB;SAC1B;QACD,OAAO,EAAE,uGAAuG;KACjH;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,mBAAmB,GAAgC;IACvD;QACE,EAAE,EAAE,sBAAsB;QAC1B,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE;YACR,aAAa,EAAU,+BAA+B;YACtD,eAAe,EAAQ,sBAAsB;YAC7C,aAAa,EAAU,SAAS;YAChC,WAAW,EAAY,MAAM;YAC7B,iBAAiB,EAAM,yBAAyB;YAChD,oBAAoB,EAAG,oBAAoB;SAC5C;QACD,OAAO,EAAE,8GAA8G;KACxH;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,YAAY,GAAgC;IAChD;QACE,EAAE,EAAE,gBAAgB;QACpB,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE;YACR,2DAA2D;YAC3D,wEAAwE;YACxE,qBAAqB,EAAc,8BAA8B;YACjE,qBAAqB,EAAc,+BAA+B;YAClE,qBAAqB,EAAc,qBAAqB;YACxD,gCAAgC,EAAG,gBAAgB;YACnD,0CAA0C,EAAE,mBAAmB;YAC/D,kBAAkB,EAAiB,oBAAoB;SACxD;QACD,OAAO,EAAE,8FAA8F;KACxG;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,yBAAyB,GAAgC;IAC7D;QACE,EAAE,EAAE,qBAAqB;QACzB,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE;YACR,0FAA0F;YAC1F,qBAAqB;YACrB,wBAAwB;YACxB,oBAAoB;YACpB,6BAA6B;YAC7B,4BAA4B;YAC5B,gCAAgC;YAChC,2DAA2D;SAC5D;QACD,OAAO,EAAE,8FAA8F;KACxG;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAgC;IAClD;QACE,EAAE,EAAE,sBAAsB;QAC1B,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE;YACR,iFAAiF;YACjF,+DAA+D;YAC/D,iEAAiE;YACjE,2EAA2E;YAC3E,wBAAwB;YACxB,+BAA+B;SAChC;QACD,OAAO,EAAE,4EAA4E;KACtF;CACF,CAAC;AAEF,2BAA2B;AAC3B,MAAM,SAAS,GAAgC;IAC7C,GAAG,eAAe;IAClB,GAAG,mBAAmB;IACtB,GAAG,YAAY;IACf,GAAG,yBAAyB;IAC5B,GAAG,cAAc;CAClB,CAAC;AAEF;;GAEG;AACH,SAAS,WAAW,CAClB,QAAgB,EAChB,OAAe;IAEf,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;YAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAE,CAAC;YAE/B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACpC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjC,IAAI,KAAK,EAAE,CAAC;oBACV,8DAA8D;oBAC9D,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAChC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,KAAK,QAAQ;wBACnB,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE;wBAClB,CAAC,CAAC,IAAI,KAAK,SAAS,GAAG,CAAC,CAC3B,CAAC;oBAEF,IAAI,CAAC,YAAY,EAAE,CAAC;wBAClB,qCAAqC;wBACrC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;wBACnD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;wBAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC;wBAE5D,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,IAAI,CAAC,EAAE;4BACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;4BACvB,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,IAAI,EAAE,SAAS,GAAG,CAAC;4BACnB,OAAO,EAAE,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO;yBACvE,CAAC,CAAC;oBACL,CAAC;oBAED,MAAM,CAAC,oDAAoD;gBAC7D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB;IAEhB,qBAAqB;IACrB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE;YAAE,OAAO,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;IAEzE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,SAAS;QAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAE7C,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACtD,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;IACjC,CAAC;IAED,mDAAmD;IACnD,MAAM,aAAa,GAA2B;QAC5C,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;KACP,CAAC;IAEF,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACtE,IAAI,OAAO,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QAClC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill file validator.
|
|
3
|
+
*
|
|
4
|
+
* Validates Markdown skill files that use YAML frontmatter for
|
|
5
|
+
* metadata. Checks for required fields, valid frontmatter syntax,
|
|
6
|
+
* and reasonable content structure.
|
|
7
|
+
*/
|
|
8
|
+
export interface SkillValidationResult {
|
|
9
|
+
path: string;
|
|
10
|
+
valid: boolean;
|
|
11
|
+
errors: string[];
|
|
12
|
+
warnings: string[];
|
|
13
|
+
metadata?: {
|
|
14
|
+
name?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
hasFrontmatter: boolean;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validate a single skill file.
|
|
21
|
+
*
|
|
22
|
+
* Checks:
|
|
23
|
+
* - File exists and is readable
|
|
24
|
+
* - Valid YAML frontmatter with `name` and `description` fields
|
|
25
|
+
* - Non-empty body content
|
|
26
|
+
* - Reasonable content length
|
|
27
|
+
*
|
|
28
|
+
* @param filePath Absolute path to a .md skill file.
|
|
29
|
+
* @returns Validation result with errors and warnings.
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateSkill(filePath: string): Promise<SkillValidationResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Validate all skill files in a directory.
|
|
34
|
+
*
|
|
35
|
+
* Looks for `.md` files in the given directory (non-recursively).
|
|
36
|
+
*
|
|
37
|
+
* @param dirPath Absolute path to a directory containing skill files.
|
|
38
|
+
* @returns Array of validation results, one per skill file.
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateSkills(dirPath: string): Promise<SkillValidationResult[]>;
|
|
41
|
+
//# sourceMappingURL=skillValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillValidator.d.ts","sourceRoot":"","sources":["../../src/core/skillValidator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AAiDD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,CAAC,CA0HhC;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,EAAE,CAAC,CA2DlC"}
|