@nahisaho/musubix-security 1.8.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/analyzers/ai/index.d.ts +6 -0
- package/dist/analyzers/ai/index.d.ts.map +1 -0
- package/dist/analyzers/ai/index.js +6 -0
- package/dist/analyzers/ai/index.js.map +1 -0
- package/dist/analyzers/ai/prompt-injection-detector.d.ts +152 -0
- package/dist/analyzers/ai/prompt-injection-detector.d.ts.map +1 -0
- package/dist/analyzers/ai/prompt-injection-detector.js +468 -0
- package/dist/analyzers/ai/prompt-injection-detector.js.map +1 -0
- package/dist/analyzers/api/api-security-analyzer.d.ts +263 -0
- package/dist/analyzers/api/api-security-analyzer.d.ts.map +1 -0
- package/dist/analyzers/api/api-security-analyzer.js +581 -0
- package/dist/analyzers/api/api-security-analyzer.js.map +1 -0
- package/dist/analyzers/compliance/compliance-checker.d.ts +201 -0
- package/dist/analyzers/compliance/compliance-checker.d.ts.map +1 -0
- package/dist/analyzers/compliance/compliance-checker.js +772 -0
- package/dist/analyzers/compliance/compliance-checker.js.map +1 -0
- package/dist/analyzers/container/image-scanner.d.ts +163 -0
- package/dist/analyzers/container/image-scanner.d.ts.map +1 -0
- package/dist/analyzers/container/image-scanner.js +459 -0
- package/dist/analyzers/container/image-scanner.js.map +1 -0
- package/dist/analyzers/container/index.d.ts +6 -0
- package/dist/analyzers/container/index.d.ts.map +1 -0
- package/dist/analyzers/container/index.js +6 -0
- package/dist/analyzers/container/index.js.map +1 -0
- package/dist/analyzers/dashboard/security-dashboard.d.ts +286 -0
- package/dist/analyzers/dashboard/security-dashboard.d.ts.map +1 -0
- package/dist/analyzers/dashboard/security-dashboard.js +796 -0
- package/dist/analyzers/dashboard/security-dashboard.js.map +1 -0
- package/dist/analyzers/iac/iac-checker.d.ts +124 -0
- package/dist/analyzers/iac/iac-checker.d.ts.map +1 -0
- package/dist/analyzers/iac/iac-checker.js +755 -0
- package/dist/analyzers/iac/iac-checker.js.map +1 -0
- package/dist/analyzers/iac/index.d.ts +6 -0
- package/dist/analyzers/iac/index.d.ts.map +1 -0
- package/dist/analyzers/iac/index.js +6 -0
- package/dist/analyzers/iac/index.js.map +1 -0
- package/dist/analyzers/index.d.ts +9 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +13 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/monitor/realtime-monitor.d.ts +216 -0
- package/dist/analyzers/monitor/realtime-monitor.d.ts.map +1 -0
- package/dist/analyzers/monitor/realtime-monitor.js +601 -0
- package/dist/analyzers/monitor/realtime-monitor.js.map +1 -0
- package/dist/analyzers/sast/index.d.ts +7 -0
- package/dist/analyzers/sast/index.d.ts.map +1 -0
- package/dist/analyzers/sast/index.js +7 -0
- package/dist/analyzers/sast/index.js.map +1 -0
- package/dist/analyzers/sast/interprocedural-analyzer.d.ts +276 -0
- package/dist/analyzers/sast/interprocedural-analyzer.d.ts.map +1 -0
- package/dist/analyzers/sast/interprocedural-analyzer.js +635 -0
- package/dist/analyzers/sast/interprocedural-analyzer.js.map +1 -0
- package/dist/analyzers/sast/zero-day-detector.d.ts +183 -0
- package/dist/analyzers/sast/zero-day-detector.d.ts.map +1 -0
- package/dist/analyzers/sast/zero-day-detector.js +593 -0
- package/dist/analyzers/sast/zero-day-detector.js.map +1 -0
- package/dist/analyzers/sca/dependency-scanner.d.ts +275 -0
- package/dist/analyzers/sca/dependency-scanner.d.ts.map +1 -0
- package/dist/analyzers/sca/dependency-scanner.js +642 -0
- package/dist/analyzers/sca/dependency-scanner.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +10 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/pipeline-manager.d.ts +105 -0
- package/dist/core/pipeline-manager.d.ts.map +1 -0
- package/dist/core/pipeline-manager.js +449 -0
- package/dist/core/pipeline-manager.js.map +1 -0
- package/dist/core/result-aggregator.d.ts +96 -0
- package/dist/core/result-aggregator.d.ts.map +1 -0
- package/dist/core/result-aggregator.js +462 -0
- package/dist/core/result-aggregator.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/ci-integration.d.ts +227 -0
- package/dist/integrations/ci-integration.d.ts.map +1 -0
- package/dist/integrations/ci-integration.js +472 -0
- package/dist/integrations/ci-integration.js.map +1 -0
- package/dist/integrations/git-hooks.d.ts +155 -0
- package/dist/integrations/git-hooks.d.ts.map +1 -0
- package/dist/integrations/git-hooks.js +425 -0
- package/dist/integrations/git-hooks.js.map +1 -0
- package/dist/integrations/index.d.ts +9 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +9 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/report-aggregator.d.ts +250 -0
- package/dist/integrations/report-aggregator.d.ts.map +1 -0
- package/dist/integrations/report-aggregator.js +488 -0
- package/dist/integrations/report-aggregator.js.map +1 -0
- package/dist/integrations/vscode-integration.d.ts +245 -0
- package/dist/integrations/vscode-integration.d.ts.map +1 -0
- package/dist/integrations/vscode-integration.js +449 -0
- package/dist/integrations/vscode-integration.js.map +1 -0
- package/dist/intelligence/attack-pattern-matcher.d.ts +217 -0
- package/dist/intelligence/attack-pattern-matcher.d.ts.map +1 -0
- package/dist/intelligence/attack-pattern-matcher.js +887 -0
- package/dist/intelligence/attack-pattern-matcher.js.map +1 -0
- package/dist/intelligence/index.d.ts +12 -0
- package/dist/intelligence/index.d.ts.map +1 -0
- package/dist/intelligence/index.js +18 -0
- package/dist/intelligence/index.js.map +1 -0
- package/dist/intelligence/neuro-symbolic-core.d.ts +88 -0
- package/dist/intelligence/neuro-symbolic-core.d.ts.map +1 -0
- package/dist/intelligence/neuro-symbolic-core.js +403 -0
- package/dist/intelligence/neuro-symbolic-core.js.map +1 -0
- package/dist/intelligence/predictive-analyzer.d.ts +317 -0
- package/dist/intelligence/predictive-analyzer.d.ts.map +1 -0
- package/dist/intelligence/predictive-analyzer.js +714 -0
- package/dist/intelligence/predictive-analyzer.js.map +1 -0
- package/dist/intelligence/risk-scorer.d.ts +333 -0
- package/dist/intelligence/risk-scorer.d.ts.map +1 -0
- package/dist/intelligence/risk-scorer.js +824 -0
- package/dist/intelligence/risk-scorer.js.map +1 -0
- package/dist/intelligence/security-analytics.d.ts +349 -0
- package/dist/intelligence/security-analytics.d.ts.map +1 -0
- package/dist/intelligence/security-analytics.js +813 -0
- package/dist/intelligence/security-analytics.js.map +1 -0
- package/dist/intelligence/threat-intelligence.d.ts +288 -0
- package/dist/intelligence/threat-intelligence.d.ts.map +1 -0
- package/dist/intelligence/threat-intelligence.js +639 -0
- package/dist/intelligence/threat-intelligence.js.map +1 -0
- package/dist/policy/index.d.ts +6 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +6 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/policy-engine.d.ts +254 -0
- package/dist/policy/policy-engine.d.ts.map +1 -0
- package/dist/policy/policy-engine.js +651 -0
- package/dist/policy/policy-engine.js.map +1 -0
- package/dist/remediation/auto-fixer.d.ts +179 -0
- package/dist/remediation/auto-fixer.d.ts.map +1 -0
- package/dist/remediation/auto-fixer.js +540 -0
- package/dist/remediation/auto-fixer.js.map +1 -0
- package/dist/remediation/fix-validator.d.ts +195 -0
- package/dist/remediation/fix-validator.d.ts.map +1 -0
- package/dist/remediation/fix-validator.js +462 -0
- package/dist/remediation/fix-validator.js.map +1 -0
- package/dist/remediation/index.d.ts +10 -0
- package/dist/remediation/index.d.ts.map +1 -0
- package/dist/remediation/index.js +15 -0
- package/dist/remediation/index.js.map +1 -0
- package/dist/remediation/patch-generator.d.ts +203 -0
- package/dist/remediation/patch-generator.d.ts.map +1 -0
- package/dist/remediation/patch-generator.js +533 -0
- package/dist/remediation/patch-generator.js.map +1 -0
- package/dist/remediation/remediation-planner.d.ts +262 -0
- package/dist/remediation/remediation-planner.d.ts.map +1 -0
- package/dist/remediation/remediation-planner.js +531 -0
- package/dist/remediation/remediation-planner.js.map +1 -0
- package/dist/remediation/secure-code-transformer.d.ts +222 -0
- package/dist/remediation/secure-code-transformer.d.ts.map +1 -0
- package/dist/remediation/secure-code-transformer.js +625 -0
- package/dist/remediation/secure-code-transformer.js.map +1 -0
- package/dist/types/fix.d.ts +3 -1
- package/dist/types/fix.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/interprocedural.d.ts +203 -0
- package/dist/types/interprocedural.d.ts.map +1 -0
- package/dist/types/interprocedural.js +7 -0
- package/dist/types/interprocedural.js.map +1 -0
- package/dist/types/neuro-symbolic.d.ts +179 -0
- package/dist/types/neuro-symbolic.d.ts.map +1 -0
- package/dist/types/neuro-symbolic.js +7 -0
- package/dist/types/neuro-symbolic.js.map +1 -0
- package/dist/types/pipeline.d.ts +173 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +7 -0
- package/dist/types/pipeline.js.map +1 -0
- package/dist/types/result.d.ts +134 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +25 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/vulnerability.d.ts +2 -2
- package/dist/types/vulnerability.d.ts.map +1 -1
- package/dist/types/zero-day.d.ts +146 -0
- package/dist/types/zero-day.d.ts.map +1 -0
- package/dist/types/zero-day.js +7 -0
- package/dist/types/zero-day.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ MUSUBIXシステムにセキュリティ特化機能を提供するパッケー
|
|
|
13
13
|
- **自動修正**: LLM(VS Code LM API)+ Z3形式検証による安全な修正
|
|
14
14
|
- **シークレット検出**: APIキー、トークン、パスワードの検出
|
|
15
15
|
- **依存関係監査**: npm依存関係の脆弱性チェック
|
|
16
|
+
- **セキュリティインテリジェンス**: 脅威フィード統合、MITRE ATT&CK対応
|
|
17
|
+
- **リスク分析**: CVSS計算、予測分析、異常検出
|
|
16
18
|
|
|
17
19
|
## インストール
|
|
18
20
|
|
|
@@ -64,6 +66,31 @@ const taintResult = await taintAnalyzer.analyze(code, 'file.ts');
|
|
|
64
66
|
const fixPipeline = new FixPipeline();
|
|
65
67
|
const fixes = await fixPipeline.generateFix(vulnerability);
|
|
66
68
|
const verified = await fixPipeline.verifyFix(fixes[0]);
|
|
69
|
+
|
|
70
|
+
// セキュリティインテリジェンス (Phase 6)
|
|
71
|
+
import {
|
|
72
|
+
ThreatIntelligence,
|
|
73
|
+
AttackPatternMatcher,
|
|
74
|
+
RiskScorer,
|
|
75
|
+
SecurityAnalytics,
|
|
76
|
+
PredictiveAnalyzer
|
|
77
|
+
} from '@nahisaho/musubix-security';
|
|
78
|
+
|
|
79
|
+
// 脅威インテリジェンス
|
|
80
|
+
const threatIntel = new ThreatIntelligence();
|
|
81
|
+
await threatIntel.addFeed({ id: 'feed-1', name: 'My Feed', url: 'https://...' });
|
|
82
|
+
const matches = threatIntel.matchCode(code);
|
|
83
|
+
|
|
84
|
+
// リスクスコアリング
|
|
85
|
+
const riskScorer = new RiskScorer();
|
|
86
|
+
const cvss = riskScorer.calculateCVSS(vulnerability);
|
|
87
|
+
const businessImpact = riskScorer.assessBusinessImpact(vulnerability);
|
|
88
|
+
|
|
89
|
+
// 予測分析
|
|
90
|
+
const predictor = new PredictiveAnalyzer();
|
|
91
|
+
predictor.addDataPoints([...historicalData]);
|
|
92
|
+
const forecast = predictor.projectRisk(30); // 30日先の予測
|
|
93
|
+
const anomalies = predictor.detectAnomalies();
|
|
67
94
|
```
|
|
68
95
|
|
|
69
96
|
## 設定
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AI Security Analyzers exports
|
|
3
|
+
* @module @nahisaho/musubix-security/analyzers/ai
|
|
4
|
+
*/
|
|
5
|
+
export { PromptInjectionDetector, createPromptInjectionDetector, type PromptInjectionResult, type PromptInjectionVulnerability, type DetectedPattern, type LLMCallSite, type LLMApiType, type PromptInjectionOptions, type InjectionPattern, } from './prompt-injection-detector.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analyzers/ai/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,GACtB,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/ai/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAQ9B,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prompt Injection Detector for LLM APIs
|
|
3
|
+
* @module @nahisaho/musubix-security/analyzers/ai/prompt-injection-detector
|
|
4
|
+
* @trace DES-SEC2-AI-001, REQ-SEC2-AI-001
|
|
5
|
+
*/
|
|
6
|
+
import type { Vulnerability, Severity, SourceLocation } from '../../types/vulnerability.js';
|
|
7
|
+
/**
|
|
8
|
+
* Prompt injection detection result
|
|
9
|
+
*/
|
|
10
|
+
export interface PromptInjectionResult {
|
|
11
|
+
vulnerability: PromptInjectionVulnerability;
|
|
12
|
+
confidence: number;
|
|
13
|
+
patterns: DetectedPattern[];
|
|
14
|
+
llmCallSite: LLMCallSite;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Prompt injection vulnerability
|
|
18
|
+
*/
|
|
19
|
+
export interface PromptInjectionVulnerability {
|
|
20
|
+
id: string;
|
|
21
|
+
type: 'direct' | 'indirect' | 'jailbreak' | 'data-exfiltration' | 'prompt-leaking';
|
|
22
|
+
severity: Severity;
|
|
23
|
+
location: SourceLocation;
|
|
24
|
+
description: string;
|
|
25
|
+
recommendation: string;
|
|
26
|
+
codeSnippet: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Detected injection pattern
|
|
30
|
+
*/
|
|
31
|
+
export interface DetectedPattern {
|
|
32
|
+
patternId: string;
|
|
33
|
+
patternName: string;
|
|
34
|
+
matchedText: string;
|
|
35
|
+
position: {
|
|
36
|
+
start: number;
|
|
37
|
+
end: number;
|
|
38
|
+
};
|
|
39
|
+
category: 'injection' | 'jailbreak' | 'obfuscation' | 'encoding';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* LLM API call site information
|
|
43
|
+
*/
|
|
44
|
+
export interface LLMCallSite {
|
|
45
|
+
apiType: LLMApiType;
|
|
46
|
+
location: SourceLocation;
|
|
47
|
+
hasInputValidation: boolean;
|
|
48
|
+
hasOutputSanitization: boolean;
|
|
49
|
+
promptSource: 'static' | 'user-input' | 'mixed' | 'unknown';
|
|
50
|
+
modelName?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Supported LLM API types
|
|
54
|
+
*/
|
|
55
|
+
export type LLMApiType = 'openai' | 'anthropic' | 'azure-openai' | 'google-ai' | 'huggingface' | 'langchain' | 'llamaindex' | 'custom';
|
|
56
|
+
/**
|
|
57
|
+
* Detection options
|
|
58
|
+
*/
|
|
59
|
+
export interface PromptInjectionOptions {
|
|
60
|
+
/** Enable heuristic detection */
|
|
61
|
+
enableHeuristics?: boolean;
|
|
62
|
+
/** Enable pattern matching */
|
|
63
|
+
enablePatterns?: boolean;
|
|
64
|
+
/** Minimum confidence threshold */
|
|
65
|
+
minConfidence?: number;
|
|
66
|
+
/** Custom patterns */
|
|
67
|
+
customPatterns?: InjectionPattern[];
|
|
68
|
+
/** Skip specific patterns */
|
|
69
|
+
skipPatterns?: string[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Injection pattern definition
|
|
73
|
+
*/
|
|
74
|
+
export interface InjectionPattern {
|
|
75
|
+
id: string;
|
|
76
|
+
name: string;
|
|
77
|
+
regex: RegExp;
|
|
78
|
+
severity: Severity;
|
|
79
|
+
category: 'injection' | 'jailbreak' | 'obfuscation' | 'encoding';
|
|
80
|
+
description: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Prompt Injection Detector
|
|
84
|
+
* @trace DES-SEC2-AI-001
|
|
85
|
+
*/
|
|
86
|
+
export declare class PromptInjectionDetector {
|
|
87
|
+
private options;
|
|
88
|
+
private patterns;
|
|
89
|
+
constructor(options?: PromptInjectionOptions);
|
|
90
|
+
/**
|
|
91
|
+
* Detect prompt injection vulnerabilities in code
|
|
92
|
+
* @trace REQ-SEC2-AI-001
|
|
93
|
+
*/
|
|
94
|
+
detect(code: string, filePath: string): Promise<PromptInjectionResult[]>;
|
|
95
|
+
/**
|
|
96
|
+
* Identify LLM API calls in code
|
|
97
|
+
*/
|
|
98
|
+
identifyLLMCalls(code: string, filePath: string): LLMCallSite[];
|
|
99
|
+
/**
|
|
100
|
+
* Check if input validation exists before LLM call
|
|
101
|
+
*/
|
|
102
|
+
checkInputValidation(code: string, callIndex: number, _lines: string[]): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Check if output sanitization exists after LLM call
|
|
105
|
+
*/
|
|
106
|
+
checkOutputSanitization(code: string, callIndex: number, _lines: string[]): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Determine prompt source type
|
|
109
|
+
*/
|
|
110
|
+
determinePromptSource(code: string, callIndex: number, _lines: string[]): 'static' | 'user-input' | 'mixed' | 'unknown';
|
|
111
|
+
/**
|
|
112
|
+
* Extract model name from code
|
|
113
|
+
*/
|
|
114
|
+
extractModelName(code: string, callIndex: number): string | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* Extract prompt context around LLM call
|
|
117
|
+
*/
|
|
118
|
+
private extractPromptContext;
|
|
119
|
+
/**
|
|
120
|
+
* Calculate detection confidence
|
|
121
|
+
*/
|
|
122
|
+
private calculateConfidence;
|
|
123
|
+
/**
|
|
124
|
+
* Determine vulnerability type
|
|
125
|
+
*/
|
|
126
|
+
private determineVulnerabilityType;
|
|
127
|
+
/**
|
|
128
|
+
* Determine severity based on context
|
|
129
|
+
*/
|
|
130
|
+
private determineSeverity;
|
|
131
|
+
/**
|
|
132
|
+
* Generate vulnerability description
|
|
133
|
+
*/
|
|
134
|
+
private generateDescription;
|
|
135
|
+
/**
|
|
136
|
+
* Generate recommendation
|
|
137
|
+
*/
|
|
138
|
+
private generateRecommendation;
|
|
139
|
+
/**
|
|
140
|
+
* Extract code snippet around location
|
|
141
|
+
*/
|
|
142
|
+
private extractCodeSnippet;
|
|
143
|
+
/**
|
|
144
|
+
* Convert results to standard vulnerability format
|
|
145
|
+
*/
|
|
146
|
+
toVulnerabilities(results: PromptInjectionResult[]): Vulnerability[];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create prompt injection detector instance
|
|
150
|
+
*/
|
|
151
|
+
export declare function createPromptInjectionDetector(options?: PromptInjectionOptions): PromptInjectionDetector;
|
|
152
|
+
//# sourceMappingURL=prompt-injection-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-injection-detector.d.ts","sourceRoot":"","sources":["../../../src/analyzers/ai/prompt-injection-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prompt Injection Detector for LLM APIs
|
|
3
|
+
* @module @nahisaho/musubix-security/analyzers/ai/prompt-injection-detector
|
|
4
|
+
* @trace DES-SEC2-AI-001, REQ-SEC2-AI-001
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Built-in injection patterns
|
|
8
|
+
*/
|
|
9
|
+
const INJECTION_PATTERNS = [
|
|
10
|
+
// Direct injection patterns
|
|
11
|
+
{
|
|
12
|
+
id: 'PINJ-001',
|
|
13
|
+
name: 'Ignore Instructions',
|
|
14
|
+
regex: /ignore\s+(all\s+)?(previous|above|prior)\s+(instructions?|prompts?|rules?)/i,
|
|
15
|
+
severity: 'high',
|
|
16
|
+
category: 'injection',
|
|
17
|
+
description: 'Attempts to make the LLM ignore previous instructions',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'PINJ-002',
|
|
21
|
+
name: 'New Instructions Override',
|
|
22
|
+
regex: /(new|updated|real)\s+instructions?:/i,
|
|
23
|
+
severity: 'high',
|
|
24
|
+
category: 'injection',
|
|
25
|
+
description: 'Attempts to inject new instructions',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'PINJ-003',
|
|
29
|
+
name: 'System Prompt Override',
|
|
30
|
+
regex: /system\s*[:=]\s*["']|<\|system\|>|<<SYS>>|<system>/i,
|
|
31
|
+
severity: 'critical',
|
|
32
|
+
category: 'injection',
|
|
33
|
+
description: 'Attempts to override system prompt',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'PINJ-004',
|
|
37
|
+
name: 'Role Manipulation',
|
|
38
|
+
regex: /you\s+are\s+(now\s+)?(a|an|the)\s+\w+|act\s+as\s+(a|an|the)|pretend\s+(to\s+be|you're)/i,
|
|
39
|
+
severity: 'medium',
|
|
40
|
+
category: 'injection',
|
|
41
|
+
description: 'Attempts to manipulate the LLM role',
|
|
42
|
+
},
|
|
43
|
+
// Jailbreak patterns
|
|
44
|
+
{
|
|
45
|
+
id: 'PINJ-005',
|
|
46
|
+
name: 'DAN Jailbreak',
|
|
47
|
+
regex: /\bDAN\b|do\s+anything\s+now|jailbreak|bypass\s+(restrictions?|filters?|safety)/i,
|
|
48
|
+
severity: 'critical',
|
|
49
|
+
category: 'jailbreak',
|
|
50
|
+
description: 'Known jailbreak technique (DAN)',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'PINJ-006',
|
|
54
|
+
name: 'Developer Mode',
|
|
55
|
+
regex: /developer\s+mode|god\s+mode|admin\s+mode|maintenance\s+mode|debug\s+mode/i,
|
|
56
|
+
severity: 'high',
|
|
57
|
+
category: 'jailbreak',
|
|
58
|
+
description: 'Attempts to enable special modes',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'PINJ-007',
|
|
62
|
+
name: 'Fictional Context',
|
|
63
|
+
regex: /let's\s+play\s+a\s+game|imagine\s+you|in\s+this\s+(story|scenario|fiction)/i,
|
|
64
|
+
severity: 'medium',
|
|
65
|
+
category: 'jailbreak',
|
|
66
|
+
description: 'Uses fictional context to bypass restrictions',
|
|
67
|
+
},
|
|
68
|
+
// Obfuscation patterns
|
|
69
|
+
{
|
|
70
|
+
id: 'PINJ-008',
|
|
71
|
+
name: 'Base64 Encoded Payload',
|
|
72
|
+
regex: /(?:[A-Za-z0-9+/]{4}){10,}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?/,
|
|
73
|
+
severity: 'medium',
|
|
74
|
+
category: 'obfuscation',
|
|
75
|
+
description: 'Potentially base64 encoded malicious payload',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'PINJ-009',
|
|
79
|
+
name: 'Unicode Obfuscation',
|
|
80
|
+
regex: /[\u200B-\u200F\u2028-\u202F\uFEFF]|&#x?[0-9a-fA-F]+;/,
|
|
81
|
+
severity: 'medium',
|
|
82
|
+
category: 'obfuscation',
|
|
83
|
+
description: 'Unicode characters used for obfuscation',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'PINJ-010',
|
|
87
|
+
name: 'Leetspeak Obfuscation',
|
|
88
|
+
regex: /1gn0r3|pr0mpt|1nstruct10n|syst3m|byp4ss|h4ck/i,
|
|
89
|
+
severity: 'low',
|
|
90
|
+
category: 'obfuscation',
|
|
91
|
+
description: 'Leetspeak obfuscation attempt',
|
|
92
|
+
},
|
|
93
|
+
// Data exfiltration
|
|
94
|
+
{
|
|
95
|
+
id: 'PINJ-011',
|
|
96
|
+
name: 'Data Exfiltration Prompt',
|
|
97
|
+
regex: /repeat\s+(back|all|the|previous)|echo\s+(back|all|everything)|show\s+(me\s+)?(the\s+)?(system\s+)?prompt/i,
|
|
98
|
+
severity: 'high',
|
|
99
|
+
category: 'injection',
|
|
100
|
+
description: 'Attempts to extract system prompt or data',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'PINJ-012',
|
|
104
|
+
name: 'Instruction Leaking',
|
|
105
|
+
regex: /what\s+(are|were)\s+your\s+(instructions?|rules?|constraints?)|reveal\s+(your\s+)?(instructions?|prompt)/i,
|
|
106
|
+
severity: 'high',
|
|
107
|
+
category: 'injection',
|
|
108
|
+
description: 'Attempts to leak instructions',
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
/**
|
|
112
|
+
* LLM API detection patterns
|
|
113
|
+
*/
|
|
114
|
+
const LLM_API_PATTERNS = {
|
|
115
|
+
openai: [
|
|
116
|
+
/openai\.chat\.completions\.create/,
|
|
117
|
+
/openai\.Completion\.create/,
|
|
118
|
+
/new\s+OpenAI\(/,
|
|
119
|
+
/ChatOpenAI\(/,
|
|
120
|
+
/import\s+.*\s+from\s+['"]openai['"]/,
|
|
121
|
+
],
|
|
122
|
+
anthropic: [
|
|
123
|
+
/anthropic\.messages\.create/,
|
|
124
|
+
/Anthropic\(/,
|
|
125
|
+
/ChatAnthropic\(/,
|
|
126
|
+
/import\s+.*\s+from\s+['"]@anthropic-ai\/sdk['"]/,
|
|
127
|
+
],
|
|
128
|
+
'azure-openai': [
|
|
129
|
+
/AzureOpenAI\(/,
|
|
130
|
+
/azure\.openai/i,
|
|
131
|
+
/azure-openai/i,
|
|
132
|
+
],
|
|
133
|
+
'google-ai': [
|
|
134
|
+
/google\.generativeai/,
|
|
135
|
+
/GenerativeModel\(/,
|
|
136
|
+
/ChatGoogleGenerativeAI\(/,
|
|
137
|
+
],
|
|
138
|
+
huggingface: [
|
|
139
|
+
/HuggingFaceHub\(/,
|
|
140
|
+
/pipeline\s*\(\s*['"]text-generation['"]/,
|
|
141
|
+
/transformers/,
|
|
142
|
+
],
|
|
143
|
+
langchain: [
|
|
144
|
+
/from\s+langchain/,
|
|
145
|
+
/ChatPromptTemplate/,
|
|
146
|
+
/LLMChain\(/,
|
|
147
|
+
/ConversationChain\(/,
|
|
148
|
+
],
|
|
149
|
+
llamaindex: [
|
|
150
|
+
/from\s+llama_index/,
|
|
151
|
+
/LlamaIndex\(/,
|
|
152
|
+
/VectorStoreIndex/,
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Prompt Injection Detector
|
|
157
|
+
* @trace DES-SEC2-AI-001
|
|
158
|
+
*/
|
|
159
|
+
export class PromptInjectionDetector {
|
|
160
|
+
options;
|
|
161
|
+
patterns;
|
|
162
|
+
constructor(options = {}) {
|
|
163
|
+
this.options = {
|
|
164
|
+
enableHeuristics: options.enableHeuristics ?? true,
|
|
165
|
+
enablePatterns: options.enablePatterns ?? true,
|
|
166
|
+
minConfidence: options.minConfidence ?? 0.5,
|
|
167
|
+
skipPatterns: options.skipPatterns ?? [],
|
|
168
|
+
};
|
|
169
|
+
this.patterns = [
|
|
170
|
+
...INJECTION_PATTERNS.filter(p => !this.options.skipPatterns?.includes(p.id)),
|
|
171
|
+
...(options.customPatterns ?? []),
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Detect prompt injection vulnerabilities in code
|
|
176
|
+
* @trace REQ-SEC2-AI-001
|
|
177
|
+
*/
|
|
178
|
+
async detect(code, filePath) {
|
|
179
|
+
const results = [];
|
|
180
|
+
const lines = code.split('\n');
|
|
181
|
+
// Find LLM API call sites
|
|
182
|
+
const callSites = this.identifyLLMCalls(code, filePath);
|
|
183
|
+
for (const callSite of callSites) {
|
|
184
|
+
// Extract prompt content around the call site
|
|
185
|
+
const promptContext = this.extractPromptContext(code, lines, callSite);
|
|
186
|
+
// Check for injection patterns
|
|
187
|
+
const detectedPatterns = [];
|
|
188
|
+
if (this.options.enablePatterns) {
|
|
189
|
+
for (const pattern of this.patterns) {
|
|
190
|
+
const matches = promptContext.matchAll(new RegExp(pattern.regex, 'gi'));
|
|
191
|
+
for (const match of matches) {
|
|
192
|
+
detectedPatterns.push({
|
|
193
|
+
patternId: pattern.id,
|
|
194
|
+
patternName: pattern.name,
|
|
195
|
+
matchedText: match[0],
|
|
196
|
+
position: {
|
|
197
|
+
start: match.index ?? 0,
|
|
198
|
+
end: (match.index ?? 0) + match[0].length,
|
|
199
|
+
},
|
|
200
|
+
category: pattern.category,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Check for lack of input validation
|
|
206
|
+
if (!callSite.hasInputValidation) {
|
|
207
|
+
const confidence = this.calculateConfidence(callSite, detectedPatterns);
|
|
208
|
+
if (confidence >= (this.options.minConfidence ?? 0.5)) {
|
|
209
|
+
const vulnerability = {
|
|
210
|
+
id: `PINJ-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
211
|
+
type: this.determineVulnerabilityType(detectedPatterns),
|
|
212
|
+
severity: this.determineSeverity(callSite, detectedPatterns),
|
|
213
|
+
location: callSite.location,
|
|
214
|
+
description: this.generateDescription(callSite, detectedPatterns),
|
|
215
|
+
recommendation: this.generateRecommendation(callSite, detectedPatterns),
|
|
216
|
+
codeSnippet: this.extractCodeSnippet(lines, callSite.location),
|
|
217
|
+
};
|
|
218
|
+
results.push({
|
|
219
|
+
vulnerability,
|
|
220
|
+
confidence,
|
|
221
|
+
patterns: detectedPatterns,
|
|
222
|
+
llmCallSite: callSite,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return results;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Identify LLM API calls in code
|
|
231
|
+
*/
|
|
232
|
+
identifyLLMCalls(code, filePath) {
|
|
233
|
+
const callSites = [];
|
|
234
|
+
const lines = code.split('\n');
|
|
235
|
+
for (const [apiType, patterns] of Object.entries(LLM_API_PATTERNS)) {
|
|
236
|
+
for (const pattern of patterns) {
|
|
237
|
+
let match;
|
|
238
|
+
const globalPattern = new RegExp(pattern.source, 'gm');
|
|
239
|
+
while ((match = globalPattern.exec(code)) !== null) {
|
|
240
|
+
const lineNumber = code.substring(0, match.index).split('\n').length;
|
|
241
|
+
const lineContent = lines[lineNumber - 1] ?? '';
|
|
242
|
+
callSites.push({
|
|
243
|
+
apiType,
|
|
244
|
+
location: {
|
|
245
|
+
file: filePath,
|
|
246
|
+
startLine: lineNumber,
|
|
247
|
+
endLine: lineNumber,
|
|
248
|
+
startColumn: 0,
|
|
249
|
+
endColumn: lineContent.length,
|
|
250
|
+
},
|
|
251
|
+
hasInputValidation: this.checkInputValidation(code, match.index, lines),
|
|
252
|
+
hasOutputSanitization: this.checkOutputSanitization(code, match.index, lines),
|
|
253
|
+
promptSource: this.determinePromptSource(code, match.index, lines),
|
|
254
|
+
modelName: this.extractModelName(code, match.index),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return callSites;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if input validation exists before LLM call
|
|
263
|
+
*/
|
|
264
|
+
checkInputValidation(code, callIndex, _lines) {
|
|
265
|
+
// Look for validation patterns before the call
|
|
266
|
+
const beforeCall = code.substring(Math.max(0, callIndex - 500), callIndex);
|
|
267
|
+
const validationPatterns = [
|
|
268
|
+
/sanitize|validate|escape|filter|clean/i,
|
|
269
|
+
/input\s*\.\s*(trim|strip|replace)/i,
|
|
270
|
+
/zod\s*\.\s*(string|object)/i,
|
|
271
|
+
/joi\s*\.\s*(string|object)/i,
|
|
272
|
+
/yup\s*\.\s*(string|object)/i,
|
|
273
|
+
/DOMPurify|xss|bleach/i,
|
|
274
|
+
/if\s*\([^)]*\.length|if\s*\([^)]*\.match/i,
|
|
275
|
+
];
|
|
276
|
+
return validationPatterns.some(p => p.test(beforeCall));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Check if output sanitization exists after LLM call
|
|
280
|
+
*/
|
|
281
|
+
checkOutputSanitization(code, callIndex, _lines) {
|
|
282
|
+
const afterCall = code.substring(callIndex, Math.min(code.length, callIndex + 500));
|
|
283
|
+
const sanitizationPatterns = [
|
|
284
|
+
/sanitize|escape|filter|clean|encode/i,
|
|
285
|
+
/JSON\.parse|JSON\.stringify/i,
|
|
286
|
+
/\.trim\(\)|\.replace\(/i,
|
|
287
|
+
/validate.*response|check.*output/i,
|
|
288
|
+
];
|
|
289
|
+
return sanitizationPatterns.some(p => p.test(afterCall));
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Determine prompt source type
|
|
293
|
+
*/
|
|
294
|
+
determinePromptSource(code, callIndex, _lines) {
|
|
295
|
+
const contextRange = code.substring(Math.max(0, callIndex - 300), callIndex + 200);
|
|
296
|
+
const userInputPatterns = [
|
|
297
|
+
/req\.body|req\.query|req\.params/,
|
|
298
|
+
/request\.json|request\.form/,
|
|
299
|
+
/user_input|userInput|user_message/,
|
|
300
|
+
/\$\{.*input.*\}|f['"].*\{.*input.*\}/,
|
|
301
|
+
/\.get\(['"].*input.*['"]\)/,
|
|
302
|
+
];
|
|
303
|
+
const staticPatterns = [
|
|
304
|
+
/const\s+prompt\s*=\s*['"`]/,
|
|
305
|
+
/prompt\s*=\s*['"`][^${}]+['"`]/,
|
|
306
|
+
];
|
|
307
|
+
const hasUserInput = userInputPatterns.some(p => p.test(contextRange));
|
|
308
|
+
const hasStatic = staticPatterns.some(p => p.test(contextRange));
|
|
309
|
+
if (hasUserInput && hasStatic)
|
|
310
|
+
return 'mixed';
|
|
311
|
+
if (hasUserInput)
|
|
312
|
+
return 'user-input';
|
|
313
|
+
if (hasStatic)
|
|
314
|
+
return 'static';
|
|
315
|
+
return 'unknown';
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Extract model name from code
|
|
319
|
+
*/
|
|
320
|
+
extractModelName(code, callIndex) {
|
|
321
|
+
const contextRange = code.substring(Math.max(0, callIndex - 100), callIndex + 200);
|
|
322
|
+
const modelPatterns = [
|
|
323
|
+
/model\s*[=:]\s*['"]([^'"]+)['"]/,
|
|
324
|
+
/model_name\s*[=:]\s*['"]([^'"]+)['"]/,
|
|
325
|
+
/gpt-[34][-\w]*/,
|
|
326
|
+
/claude-[23][-\w]*/,
|
|
327
|
+
/gemini[-\w]*/,
|
|
328
|
+
];
|
|
329
|
+
for (const pattern of modelPatterns) {
|
|
330
|
+
const match = contextRange.match(pattern);
|
|
331
|
+
if (match) {
|
|
332
|
+
return match[1] ?? match[0];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Extract prompt context around LLM call
|
|
339
|
+
*/
|
|
340
|
+
extractPromptContext(_code, lines, callSite) {
|
|
341
|
+
const startIdx = Math.max(0, callSite.location.startLine - 20);
|
|
342
|
+
const endIdx = Math.min(lines.length, callSite.location.endLine + 10);
|
|
343
|
+
return lines.slice(startIdx, endIdx).join('\n');
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Calculate detection confidence
|
|
347
|
+
*/
|
|
348
|
+
calculateConfidence(callSite, patterns) {
|
|
349
|
+
let confidence = 0.3; // Base confidence for unvalidated LLM call
|
|
350
|
+
// Increase for user input source
|
|
351
|
+
if (callSite.promptSource === 'user-input') {
|
|
352
|
+
confidence += 0.4;
|
|
353
|
+
}
|
|
354
|
+
else if (callSite.promptSource === 'mixed') {
|
|
355
|
+
confidence += 0.2;
|
|
356
|
+
}
|
|
357
|
+
// Increase for detected patterns
|
|
358
|
+
confidence += Math.min(patterns.length * 0.1, 0.3);
|
|
359
|
+
// Decrease if output is sanitized
|
|
360
|
+
if (callSite.hasOutputSanitization) {
|
|
361
|
+
confidence -= 0.1;
|
|
362
|
+
}
|
|
363
|
+
return Math.min(Math.max(confidence, 0), 1);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Determine vulnerability type
|
|
367
|
+
*/
|
|
368
|
+
determineVulnerabilityType(patterns) {
|
|
369
|
+
const categories = patterns.map(p => p.category);
|
|
370
|
+
if (categories.includes('jailbreak'))
|
|
371
|
+
return 'jailbreak';
|
|
372
|
+
if (patterns.some(p => p.patternId.includes('011') || p.patternId.includes('012'))) {
|
|
373
|
+
return 'prompt-leaking';
|
|
374
|
+
}
|
|
375
|
+
return 'direct';
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Determine severity based on context
|
|
379
|
+
*/
|
|
380
|
+
determineSeverity(callSite, patterns) {
|
|
381
|
+
// Critical if direct user input without validation
|
|
382
|
+
if (callSite.promptSource === 'user-input' && !callSite.hasInputValidation) {
|
|
383
|
+
return 'critical';
|
|
384
|
+
}
|
|
385
|
+
// High if patterns detected
|
|
386
|
+
const patternSeverities = patterns.map(p => {
|
|
387
|
+
const patternDef = this.patterns.find(pd => pd.id === p.patternId);
|
|
388
|
+
return patternDef?.severity ?? 'medium';
|
|
389
|
+
});
|
|
390
|
+
if (patternSeverities.includes('critical'))
|
|
391
|
+
return 'critical';
|
|
392
|
+
if (patternSeverities.includes('high'))
|
|
393
|
+
return 'high';
|
|
394
|
+
// Medium for mixed sources
|
|
395
|
+
if (callSite.promptSource === 'mixed')
|
|
396
|
+
return 'medium';
|
|
397
|
+
return 'low';
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Generate vulnerability description
|
|
401
|
+
*/
|
|
402
|
+
generateDescription(callSite, patterns) {
|
|
403
|
+
const parts = [];
|
|
404
|
+
parts.push(`LLM API call (${callSite.apiType}) detected`);
|
|
405
|
+
if (callSite.promptSource === 'user-input') {
|
|
406
|
+
parts.push('with user-controlled input');
|
|
407
|
+
}
|
|
408
|
+
if (!callSite.hasInputValidation) {
|
|
409
|
+
parts.push('without input validation');
|
|
410
|
+
}
|
|
411
|
+
if (patterns.length > 0) {
|
|
412
|
+
parts.push(`- ${patterns.length} potential injection pattern(s) found`);
|
|
413
|
+
}
|
|
414
|
+
return parts.join(' ');
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Generate recommendation
|
|
418
|
+
*/
|
|
419
|
+
generateRecommendation(callSite, _patterns) {
|
|
420
|
+
const recommendations = [];
|
|
421
|
+
if (!callSite.hasInputValidation) {
|
|
422
|
+
recommendations.push('Implement input validation and sanitization before passing to LLM');
|
|
423
|
+
}
|
|
424
|
+
if (callSite.promptSource === 'user-input') {
|
|
425
|
+
recommendations.push('Use parameterized prompts with strict input boundaries');
|
|
426
|
+
recommendations.push('Implement rate limiting and monitoring for suspicious patterns');
|
|
427
|
+
}
|
|
428
|
+
if (!callSite.hasOutputSanitization) {
|
|
429
|
+
recommendations.push('Validate and sanitize LLM output before using in application');
|
|
430
|
+
}
|
|
431
|
+
recommendations.push('Consider using guardrails or content filtering');
|
|
432
|
+
return recommendations.join('. ');
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Extract code snippet around location
|
|
436
|
+
*/
|
|
437
|
+
extractCodeSnippet(lines, location) {
|
|
438
|
+
const startLine = Math.max(0, location.startLine - 3);
|
|
439
|
+
const endLine = Math.min(lines.length, location.endLine + 3);
|
|
440
|
+
return lines.slice(startLine, endLine).join('\n');
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Convert results to standard vulnerability format
|
|
444
|
+
*/
|
|
445
|
+
toVulnerabilities(results) {
|
|
446
|
+
return results.map(result => ({
|
|
447
|
+
id: result.vulnerability.id,
|
|
448
|
+
type: 'prompt-injection',
|
|
449
|
+
severity: result.vulnerability.severity,
|
|
450
|
+
cwes: ['CWE-74', 'CWE-79'], // Injection, XSS
|
|
451
|
+
owasp: ['A03:2021'], // Injection
|
|
452
|
+
location: result.vulnerability.location,
|
|
453
|
+
description: result.vulnerability.description,
|
|
454
|
+
recommendation: result.vulnerability.recommendation,
|
|
455
|
+
confidence: result.confidence,
|
|
456
|
+
ruleId: result.patterns[0]?.patternId ?? 'PINJ-HEURISTIC',
|
|
457
|
+
codeSnippet: result.vulnerability.codeSnippet,
|
|
458
|
+
detectedAt: new Date(),
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Create prompt injection detector instance
|
|
464
|
+
*/
|
|
465
|
+
export function createPromptInjectionDetector(options) {
|
|
466
|
+
return new PromptInjectionDetector(options);
|
|
467
|
+
}
|
|
468
|
+
//# sourceMappingURL=prompt-injection-detector.js.map
|