@boshu2/vibe-check 2.2.1 → 2.4.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/.agents/plans/2025-12-28-ai-safety-integration-plan.md +326 -0
- package/.agents/plans/2025-12-29-complexity-driver-plan.md +225 -0
- package/.agents/plans/2025-12-29-complexity-drivers-plan.md +253 -0
- package/.agents/research/2025-12-28-ai-platform-security-integration.md +295 -0
- package/.agents/research/2025-12-29-complexity-driver-architecture.md +392 -0
- package/.agents/research/2025-12-29-complexity-drivers.md +227 -0
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +62 -0
- package/.beads/interactions.jsonl +0 -0
- package/.beads/issues.jsonl +21 -0
- package/.beads/metadata.json +4 -0
- package/.gitattributes +3 -0
- package/AGENTS.md +40 -0
- package/CHANGELOG.md +69 -0
- package/CLAUDE.md +75 -0
- package/README.md +71 -0
- package/dist/ai-safety/contract-drift.d.ts +14 -0
- package/dist/ai-safety/contract-drift.d.ts.map +1 -0
- package/dist/ai-safety/contract-drift.js +230 -0
- package/dist/ai-safety/contract-drift.js.map +1 -0
- package/dist/ai-safety/index.d.ts +43 -0
- package/dist/ai-safety/index.d.ts.map +1 -0
- package/dist/ai-safety/index.js +177 -0
- package/dist/ai-safety/index.js.map +1 -0
- package/dist/ai-safety/scope-violation.d.ts +18 -0
- package/dist/ai-safety/scope-violation.d.ts.map +1 -0
- package/dist/ai-safety/scope-violation.js +150 -0
- package/dist/ai-safety/scope-violation.js.map +1 -0
- package/dist/ai-safety/secret-leakage.d.ts +18 -0
- package/dist/ai-safety/secret-leakage.d.ts.map +1 -0
- package/dist/ai-safety/secret-leakage.js +188 -0
- package/dist/ai-safety/secret-leakage.js.map +1 -0
- package/dist/ai-safety/token-spiral.d.ts +17 -0
- package/dist/ai-safety/token-spiral.d.ts.map +1 -0
- package/dist/ai-safety/token-spiral.js +183 -0
- package/dist/ai-safety/token-spiral.js.map +1 -0
- package/dist/ai-safety/types.d.ts +122 -0
- package/dist/ai-safety/types.d.ts.map +1 -0
- package/dist/ai-safety/types.js +32 -0
- package/dist/ai-safety/types.js.map +1 -0
- package/dist/analyzers/complexity.d.ts +92 -0
- package/dist/analyzers/complexity.d.ts.map +1 -0
- package/dist/analyzers/complexity.js +79 -0
- package/dist/analyzers/complexity.js.map +1 -0
- package/dist/analyzers/modularity.d.ts +3 -1
- package/dist/analyzers/modularity.d.ts.map +1 -1
- package/dist/analyzers/modularity.js +32 -6
- package/dist/analyzers/modularity.js.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/driver.d.ts +18 -0
- package/dist/commands/driver.d.ts.map +1 -0
- package/dist/commands/driver.js +58 -0
- package/dist/commands/driver.js.map +1 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/modularity.d.ts +2 -0
- package/dist/commands/modularity.d.ts.map +1 -1
- package/dist/commands/modularity.js +86 -7
- package/dist/commands/modularity.js.map +1 -1
- package/dist/commands/session.d.ts +9 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +42 -0
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +59 -0
- package/dist/commands/watch.js.map +1 -1
- package/drivers/README.md +327 -0
- package/drivers/go.sh +131 -0
- package/drivers/java.sh +137 -0
- package/drivers/javascript.sh +134 -0
- package/drivers/php.sh +132 -0
- package/drivers/python.sh +90 -0
- package/drivers/rust.sh +132 -0
- package/package.json +4 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret Leakage Detector
|
|
3
|
+
*
|
|
4
|
+
* Scans commit diffs for exposed secrets: API keys, tokens, credentials.
|
|
5
|
+
* Ported from ai-platform/tests/agents/test_agent_security.py
|
|
6
|
+
*/
|
|
7
|
+
import { Commit } from '../types.js';
|
|
8
|
+
import { SecretLeakageResult, AISafetyConfig } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Detect secrets leaked in commit diffs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectSecretLeakage(repoPath: string, commits: Commit[], config?: Partial<AISafetyConfig>): Promise<SecretLeakageResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Synchronous version that works with already-fetched commit data.
|
|
15
|
+
* For use when diffs aren't available (e.g., from JSONL timeline).
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectSecretLeakageFromMessages(commits: Commit[], config?: Partial<AISafetyConfig>): SecretLeakageResult;
|
|
18
|
+
//# sourceMappingURL=secret-leakage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-leakage.d.ts","sourceRoot":"","sources":["../../src/ai-safety/secret-leakage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,mBAAmB,EAGnB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAwF9B;AAoDD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,mBAAmB,CAyDrB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret Leakage Detector
|
|
3
|
+
*
|
|
4
|
+
* Scans commit diffs for exposed secrets: API keys, tokens, credentials.
|
|
5
|
+
* Ported from ai-platform/tests/agents/test_agent_security.py
|
|
6
|
+
*/
|
|
7
|
+
import { simpleGit } from 'simple-git';
|
|
8
|
+
import { SECRET_PATTERNS, } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Detect secrets leaked in commit diffs.
|
|
11
|
+
*/
|
|
12
|
+
export async function detectSecretLeakage(repoPath, commits, config = {}) {
|
|
13
|
+
if (!config.secretDetectionEnabled) {
|
|
14
|
+
return {
|
|
15
|
+
detected: false,
|
|
16
|
+
findings: [],
|
|
17
|
+
totalFindings: 0,
|
|
18
|
+
criticalFindings: 0,
|
|
19
|
+
message: 'Secret detection disabled',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const findings = [];
|
|
23
|
+
const git = simpleGit(repoPath);
|
|
24
|
+
// Merge custom patterns if provided
|
|
25
|
+
const patterns = {
|
|
26
|
+
...SECRET_PATTERNS,
|
|
27
|
+
...(config.customSecretPatterns || {}),
|
|
28
|
+
};
|
|
29
|
+
for (const commit of commits) {
|
|
30
|
+
try {
|
|
31
|
+
let contentToScan;
|
|
32
|
+
if (config.secretScanDepth === 'shallow') {
|
|
33
|
+
// Only scan commit message
|
|
34
|
+
contentToScan = commit.message;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// Full scan: get diff content using simple-git
|
|
38
|
+
const diff = await git.show([commit.hash, '--format=', '--patch']);
|
|
39
|
+
contentToScan = diff;
|
|
40
|
+
}
|
|
41
|
+
// Scan for each secret pattern
|
|
42
|
+
for (const [patternName, regex] of Object.entries(patterns)) {
|
|
43
|
+
// Reset regex state
|
|
44
|
+
regex.lastIndex = 0;
|
|
45
|
+
let match;
|
|
46
|
+
while ((match = regex.exec(contentToScan)) !== null) {
|
|
47
|
+
const matchedText = match[0];
|
|
48
|
+
const startIndex = match.index;
|
|
49
|
+
// Get line context (20 chars before and after)
|
|
50
|
+
const contextStart = Math.max(0, startIndex - 20);
|
|
51
|
+
const contextEnd = Math.min(contentToScan.length, startIndex + matchedText.length + 20);
|
|
52
|
+
const rawContext = contentToScan.substring(contextStart, contextEnd);
|
|
53
|
+
// Mask the secret in context (show first 8 chars only)
|
|
54
|
+
const maskedSecret = maskSecret(matchedText);
|
|
55
|
+
const lineContext = rawContext.replace(matchedText, maskedSecret);
|
|
56
|
+
// Determine file from diff context
|
|
57
|
+
const file = extractFileFromDiff(contentToScan, startIndex);
|
|
58
|
+
findings.push({
|
|
59
|
+
commitHash: commit.hash,
|
|
60
|
+
commitMessage: commit.message,
|
|
61
|
+
timestamp: commit.date,
|
|
62
|
+
file: file || 'unknown',
|
|
63
|
+
pattern: patternName,
|
|
64
|
+
matchedText: maskedSecret,
|
|
65
|
+
lineContext: lineContext.replace(/\n/g, ' ').trim(),
|
|
66
|
+
severity: determineSeverity(patternName),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Skip commits we can't analyze
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const criticalFindings = findings.filter((f) => f.severity === 'critical').length;
|
|
77
|
+
const detected = findings.length > 0;
|
|
78
|
+
let message = 'No secrets detected';
|
|
79
|
+
if (detected) {
|
|
80
|
+
message = `${findings.length} secret(s) found (${criticalFindings} critical)`;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
detected,
|
|
84
|
+
findings,
|
|
85
|
+
totalFindings: findings.length,
|
|
86
|
+
criticalFindings,
|
|
87
|
+
message,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Mask a secret, showing only first 8 characters.
|
|
92
|
+
*/
|
|
93
|
+
function maskSecret(secret) {
|
|
94
|
+
if (secret.length <= 8) {
|
|
95
|
+
return '***';
|
|
96
|
+
}
|
|
97
|
+
return secret.substring(0, 8) + '***';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract filename from diff context.
|
|
101
|
+
* Looks for lines like "+++ b/path/to/file.ts"
|
|
102
|
+
*/
|
|
103
|
+
function extractFileFromDiff(diff, position) {
|
|
104
|
+
// Find the nearest "+++ b/" before the match position
|
|
105
|
+
const beforeMatch = diff.substring(0, position);
|
|
106
|
+
const lines = beforeMatch.split('\n');
|
|
107
|
+
// Search backwards for file header
|
|
108
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
109
|
+
const line = lines[i];
|
|
110
|
+
if (line.startsWith('+++ b/')) {
|
|
111
|
+
return line.substring(6); // Remove "+++ b/"
|
|
112
|
+
}
|
|
113
|
+
if (line.startsWith('diff --git')) {
|
|
114
|
+
// Found diff header without +++ b/, extract from diff line
|
|
115
|
+
const match = line.match(/diff --git a\/.+ b\/(.+)/);
|
|
116
|
+
return match ? match[1] : null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Determine severity based on pattern type.
|
|
123
|
+
*/
|
|
124
|
+
function determineSeverity(patternName) {
|
|
125
|
+
// High-value secrets are critical
|
|
126
|
+
const criticalPatterns = [
|
|
127
|
+
'OpenAI API Key',
|
|
128
|
+
'GitHub Personal Access Token',
|
|
129
|
+
'GitLab Personal Access Token',
|
|
130
|
+
'AWS Access Key',
|
|
131
|
+
];
|
|
132
|
+
return criticalPatterns.includes(patternName) ? 'critical' : 'warning';
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Synchronous version that works with already-fetched commit data.
|
|
136
|
+
* For use when diffs aren't available (e.g., from JSONL timeline).
|
|
137
|
+
*/
|
|
138
|
+
export function detectSecretLeakageFromMessages(commits, config = {}) {
|
|
139
|
+
if (!config.secretDetectionEnabled) {
|
|
140
|
+
return {
|
|
141
|
+
detected: false,
|
|
142
|
+
findings: [],
|
|
143
|
+
totalFindings: 0,
|
|
144
|
+
criticalFindings: 0,
|
|
145
|
+
message: 'Secret detection disabled',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const findings = [];
|
|
149
|
+
const patterns = {
|
|
150
|
+
...SECRET_PATTERNS,
|
|
151
|
+
...(config.customSecretPatterns || {}),
|
|
152
|
+
};
|
|
153
|
+
for (const commit of commits) {
|
|
154
|
+
const contentToScan = commit.message;
|
|
155
|
+
for (const [patternName, regex] of Object.entries(patterns)) {
|
|
156
|
+
regex.lastIndex = 0;
|
|
157
|
+
let match;
|
|
158
|
+
while ((match = regex.exec(contentToScan)) !== null) {
|
|
159
|
+
const matchedText = match[0];
|
|
160
|
+
const maskedSecret = maskSecret(matchedText);
|
|
161
|
+
findings.push({
|
|
162
|
+
commitHash: commit.hash,
|
|
163
|
+
commitMessage: commit.message,
|
|
164
|
+
timestamp: commit.date,
|
|
165
|
+
file: 'commit-message',
|
|
166
|
+
pattern: patternName,
|
|
167
|
+
matchedText: maskedSecret,
|
|
168
|
+
lineContext: commit.message.substring(0, 80),
|
|
169
|
+
severity: determineSeverity(patternName),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const criticalFindings = findings.filter((f) => f.severity === 'critical').length;
|
|
175
|
+
const detected = findings.length > 0;
|
|
176
|
+
let message = 'No secrets in commit messages';
|
|
177
|
+
if (detected) {
|
|
178
|
+
message = `${findings.length} secret(s) in messages (${criticalFindings} critical)`;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
detected,
|
|
182
|
+
findings,
|
|
183
|
+
totalFindings: findings.length,
|
|
184
|
+
criticalFindings,
|
|
185
|
+
message,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=secret-leakage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-leakage.js","sourceRoot":"","sources":["../../src/ai-safety/secret-leakage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Spiral Detector
|
|
3
|
+
*
|
|
4
|
+
* Estimates token usage from commit size and detects explosion patterns.
|
|
5
|
+
* Uses heuristic: ~4 characters per token (GPT-style tokenization).
|
|
6
|
+
* Detects when commits exceed baseline by configurable threshold.
|
|
7
|
+
*/
|
|
8
|
+
import { Commit } from '../types.js';
|
|
9
|
+
import { TokenSpiralResult, AISafetyConfig } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Detect token spirals in commit history.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectTokenSpiral(commits: Commit[], lineStatsPerCommit: Map<string, {
|
|
14
|
+
additions: number;
|
|
15
|
+
deletions: number;
|
|
16
|
+
}>, filesPerCommit: Map<string, string[]>, config?: Partial<AISafetyConfig>): TokenSpiralResult;
|
|
17
|
+
//# sourceMappingURL=token-spiral.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-spiral.d.ts","sourceRoot":"","sources":["../../src/ai-safety/token-spiral.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,iBAAiB,EAGjB,cAAc,EACf,MAAM,YAAY,CAAC;AA+EpB;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EAAE,EACjB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EACzE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,iBAAiB,CAwJnB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Spiral Detector
|
|
3
|
+
*
|
|
4
|
+
* Estimates token usage from commit size and detects explosion patterns.
|
|
5
|
+
* Uses heuristic: ~4 characters per token (GPT-style tokenization).
|
|
6
|
+
* Detects when commits exceed baseline by configurable threshold.
|
|
7
|
+
*/
|
|
8
|
+
// Token estimation constants
|
|
9
|
+
const CHARS_PER_TOKEN = 4; // Approximate for GPT-style tokenization
|
|
10
|
+
const TOKENS_PER_LINE = 15; // Approximate tokens per line of code
|
|
11
|
+
const BASELINE_COMMIT_COUNT = 5; // Number of commits for baseline calculation
|
|
12
|
+
// Thresholds for indicators
|
|
13
|
+
const LARGE_DIFF_THRESHOLD = 500; // lines
|
|
14
|
+
const MANY_FILES_THRESHOLD = 10; // files
|
|
15
|
+
const MASSIVE_ADDITIONS_THRESHOLD = 1000; // lines added
|
|
16
|
+
/**
|
|
17
|
+
* Estimate tokens for a single commit based on diff stats.
|
|
18
|
+
*/
|
|
19
|
+
function estimateCommitTokens(additions, deletions, fileCount, messageLength, method) {
|
|
20
|
+
switch (method) {
|
|
21
|
+
case 'char_count':
|
|
22
|
+
// Estimate based on approximate character count
|
|
23
|
+
// Assume ~40 chars per line of code
|
|
24
|
+
const estimatedChars = (additions + deletions) * 40 + messageLength;
|
|
25
|
+
return Math.round(estimatedChars / CHARS_PER_TOKEN);
|
|
26
|
+
case 'lines':
|
|
27
|
+
// Estimate based on line count
|
|
28
|
+
return Math.round((additions + deletions) * TOKENS_PER_LINE);
|
|
29
|
+
case 'diff_size':
|
|
30
|
+
// Hybrid: consider files, lines, and message
|
|
31
|
+
const lineTokens = (additions + deletions) * TOKENS_PER_LINE;
|
|
32
|
+
const fileTokens = fileCount * 50; // ~50 tokens per file header
|
|
33
|
+
const msgTokens = Math.round(messageLength / CHARS_PER_TOKEN);
|
|
34
|
+
return Math.round(lineTokens + fileTokens + msgTokens);
|
|
35
|
+
default:
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Identify indicators for why a commit might be spiraling.
|
|
41
|
+
*/
|
|
42
|
+
function identifyIndicators(additions, deletions, fileCount) {
|
|
43
|
+
const indicators = [];
|
|
44
|
+
const totalLines = additions + deletions;
|
|
45
|
+
if (totalLines > LARGE_DIFF_THRESHOLD) {
|
|
46
|
+
indicators.push('large_diff');
|
|
47
|
+
}
|
|
48
|
+
if (fileCount > MANY_FILES_THRESHOLD) {
|
|
49
|
+
indicators.push('many_files');
|
|
50
|
+
}
|
|
51
|
+
if (additions > MASSIVE_ADDITIONS_THRESHOLD) {
|
|
52
|
+
indicators.push('massive_additions');
|
|
53
|
+
}
|
|
54
|
+
if (deletions > additions * 2) {
|
|
55
|
+
indicators.push('heavy_deletions');
|
|
56
|
+
}
|
|
57
|
+
if (additions > deletions * 5 && additions > 200) {
|
|
58
|
+
indicators.push('bulk_additions');
|
|
59
|
+
}
|
|
60
|
+
return indicators;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Detect token spirals in commit history.
|
|
64
|
+
*/
|
|
65
|
+
export function detectTokenSpiral(commits, lineStatsPerCommit, filesPerCommit, config = {}) {
|
|
66
|
+
// Check if token tracking is enabled
|
|
67
|
+
if (config.tokenTrackingEnabled === false) {
|
|
68
|
+
return {
|
|
69
|
+
detected: false,
|
|
70
|
+
estimatedTokens: {
|
|
71
|
+
totalTokens: 0,
|
|
72
|
+
averagePerCommit: 0,
|
|
73
|
+
peakCommit: null,
|
|
74
|
+
baseline: 0,
|
|
75
|
+
explosionDetected: false,
|
|
76
|
+
},
|
|
77
|
+
spirals: [],
|
|
78
|
+
message: 'Token tracking disabled',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (commits.length === 0) {
|
|
82
|
+
return {
|
|
83
|
+
detected: false,
|
|
84
|
+
estimatedTokens: {
|
|
85
|
+
totalTokens: 0,
|
|
86
|
+
averagePerCommit: 0,
|
|
87
|
+
peakCommit: null,
|
|
88
|
+
baseline: 0,
|
|
89
|
+
explosionDetected: false,
|
|
90
|
+
},
|
|
91
|
+
spirals: [],
|
|
92
|
+
message: 'No commits to analyze',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const method = config.tokenEstimationMethod ?? 'char_count';
|
|
96
|
+
const explosionThreshold = config.explosionThreshold ?? 2.0;
|
|
97
|
+
// Sort commits by date (oldest first)
|
|
98
|
+
const sortedCommits = [...commits].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
99
|
+
// Calculate token estimates per commit
|
|
100
|
+
const tokenEstimates = [];
|
|
101
|
+
for (const commit of sortedCommits) {
|
|
102
|
+
const lineStats = lineStatsPerCommit.get(commit.hash) || { additions: 0, deletions: 0 };
|
|
103
|
+
const files = filesPerCommit.get(commit.hash) || [];
|
|
104
|
+
const tokens = estimateCommitTokens(lineStats.additions, lineStats.deletions, files.length, commit.message.length, method);
|
|
105
|
+
tokenEstimates.push({
|
|
106
|
+
commit,
|
|
107
|
+
tokens,
|
|
108
|
+
additions: lineStats.additions,
|
|
109
|
+
deletions: lineStats.deletions,
|
|
110
|
+
fileCount: files.length,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Calculate baseline from first N commits
|
|
114
|
+
const baselineCommits = tokenEstimates.slice(0, BASELINE_COMMIT_COUNT);
|
|
115
|
+
const baseline = baselineCommits.length > 0
|
|
116
|
+
? baselineCommits.reduce((sum, c) => sum + c.tokens, 0) / baselineCommits.length
|
|
117
|
+
: 0;
|
|
118
|
+
// Find peak commit
|
|
119
|
+
let peakCommit = null;
|
|
120
|
+
let maxTokens = 0;
|
|
121
|
+
for (const est of tokenEstimates) {
|
|
122
|
+
if (est.tokens > maxTokens) {
|
|
123
|
+
maxTokens = est.tokens;
|
|
124
|
+
peakCommit = {
|
|
125
|
+
hash: est.commit.hash,
|
|
126
|
+
tokens: est.tokens,
|
|
127
|
+
message: est.commit.message,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Calculate totals
|
|
132
|
+
const totalTokens = tokenEstimates.reduce((sum, c) => sum + c.tokens, 0);
|
|
133
|
+
const averagePerCommit = tokenEstimates.length > 0
|
|
134
|
+
? Math.round(totalTokens / tokenEstimates.length)
|
|
135
|
+
: 0;
|
|
136
|
+
// Detect spirals (commits exceeding baseline by threshold)
|
|
137
|
+
const spirals = [];
|
|
138
|
+
let explosionDetected = false;
|
|
139
|
+
// Only check commits after the baseline window
|
|
140
|
+
const commitsToCheck = tokenEstimates.slice(BASELINE_COMMIT_COUNT);
|
|
141
|
+
for (const est of commitsToCheck) {
|
|
142
|
+
if (baseline > 0) {
|
|
143
|
+
const multiplier = est.tokens / baseline;
|
|
144
|
+
if (multiplier >= explosionThreshold) {
|
|
145
|
+
explosionDetected = true;
|
|
146
|
+
const indicators = identifyIndicators(est.additions, est.deletions, est.fileCount);
|
|
147
|
+
spirals.push({
|
|
148
|
+
commitHash: est.commit.hash,
|
|
149
|
+
commitMessage: est.commit.message,
|
|
150
|
+
timestamp: est.commit.date,
|
|
151
|
+
estimatedTokens: est.tokens,
|
|
152
|
+
baselineMultiplier: Math.round(multiplier * 10) / 10,
|
|
153
|
+
indicators,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Generate message
|
|
159
|
+
const detected = spirals.length > 0;
|
|
160
|
+
let message = 'Token usage within normal range';
|
|
161
|
+
if (detected) {
|
|
162
|
+
if (explosionDetected) {
|
|
163
|
+
const maxMultiplier = Math.max(...spirals.map(s => s.baselineMultiplier));
|
|
164
|
+
message = `Token explosion detected: ${spirals.length} commit(s) exceeded ${explosionThreshold}x baseline (max: ${maxMultiplier}x)`;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
message = `${spirals.length} commit(s) with elevated token usage`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
detected,
|
|
172
|
+
estimatedTokens: {
|
|
173
|
+
totalTokens,
|
|
174
|
+
averagePerCommit,
|
|
175
|
+
peakCommit,
|
|
176
|
+
baseline: Math.round(baseline),
|
|
177
|
+
explosionDetected,
|
|
178
|
+
},
|
|
179
|
+
spirals,
|
|
180
|
+
message,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=token-spiral.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-spiral.js","sourceRoot":"","sources":["../../src/ai-safety/token-spiral.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,6BAA6B;AAC7B,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,yCAAyC;AACpE,MAAM,eAAe,GAAG,EAAE,CAAC,CAAC,sCAAsC;AAClE,MAAM,qBAAqB,GAAG,CAAC,CAAC,CAAC,6CAA6C;AAE9E,4BAA4B;AAC5B,MAAM,oBAAoB,GAAG,GAAG,CAAC,CAAC,QAAQ;AAC1C,MAAM,oBAAoB,GAAG,EAAE,CAAC,CAAC,QAAQ;AACzC,MAAM,2BAA2B,GAAG,IAAI,CAAC,CAAC,cAAc;AAExD;;GAEG;AACH,SAAS,oBAAoB,CAC3B,SAAiB,EACjB,SAAiB,EACjB,SAAiB,EACjB,aAAqB,EACrB,MAA4C;IAE5C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,YAAY;YACf,gDAAgD;YAChD,oCAAoC;YACpC,MAAM,cAAc,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC;YACpE,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,eAAe,CAAC,CAAC;QAEtD,KAAK,OAAO;YACV,+BAA+B;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC,CAAC;QAE/D,KAAK,WAAW;YACd,6CAA6C;YAC7C,MAAM,UAAU,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC;YAC7D,MAAM,UAAU,GAAG,SAAS,GAAG,EAAE,CAAC,CAAC,6BAA6B;YAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,eAAe,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC,CAAC;QAEzD;YACE,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CACzB,SAAiB,EACjB,SAAiB,EACjB,SAAiB;IAEjB,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,IAAI,UAAU,GAAG,oBAAoB,EAAE,CAAC;QACtC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,SAAS,GAAG,oBAAoB,EAAE,CAAC;QACrC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,SAAS,GAAG,2BAA2B,EAAE,CAAC;QAC5C,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,SAAS,GAAG,SAAS,GAAG,CAAC,EAAE,CAAC;QAC9B,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,SAAS,GAAG,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QACjD,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAiB,EACjB,kBAAyE,EACzE,cAAqC,EACrC,SAAkC,EAAE;IAEpC,qCAAqC;IACrC,IAAI,MAAM,CAAC,oBAAoB,KAAK,KAAK,EAAE,CAAC;QAC1C,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,eAAe,EAAE;gBACf,WAAW,EAAE,CAAC;gBACd,gBAAgB,EAAE,CAAC;gBACnB,UAAU,EAAE,IAAI;gBAChB,QAAQ,EAAE,CAAC;gBACX,iBAAiB,EAAE,KAAK;aACzB;YACD,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,yBAAyB;SACnC,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,eAAe,EAAE;gBACf,WAAW,EAAE,CAAC;gBACd,gBAAgB,EAAE,CAAC;gBACnB,UAAU,EAAE,IAAI;gBAChB,QAAQ,EAAE,CAAC;gBACX,iBAAiB,EAAE,KAAK;aACzB;YACD,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,uBAAuB;SACjC,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,qBAAqB,IAAI,YAAY,CAAC;IAC5D,MAAM,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,IAAI,GAAG,CAAC;IAE5D,sCAAsC;IACtC,MAAM,aAAa,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAC9C,CAAC;IAEF,uCAAuC;IACvC,MAAM,cAAc,GAMf,EAAE,CAAC;IAER,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;QACxF,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,oBAAoB,CACjC,SAAS,CAAC,SAAS,EACnB,SAAS,CAAC,SAAS,EACnB,KAAK,CAAC,MAAM,EACZ,MAAM,CAAC,OAAO,CAAC,MAAM,EACrB,MAAM,CACP,CAAC;QAEF,cAAc,CAAC,IAAI,CAAC;YAClB,MAAM;YACN,MAAM;YACN,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,SAAS,EAAE,KAAK,CAAC,MAAM;SACxB,CAAC,CAAC;IACL,CAAC;IAED,0CAA0C;IAC1C,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC;QACzC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,eAAe,CAAC,MAAM;QAChF,CAAC,CAAC,CAAC,CAAC;IAEN,mBAAmB;IACnB,IAAI,UAAU,GAAgC,IAAI,CAAC;IACnD,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,IAAI,GAAG,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC;YACvB,UAAU,GAAG;gBACX,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI;gBACrB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO;aAC5B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACzE,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC;QAChD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC;QACjD,CAAC,CAAC,CAAC,CAAC;IAEN,2DAA2D;IAC3D,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAE9B,+CAA+C;IAC/C,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAEnE,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC;YAEzC,IAAI,UAAU,IAAI,kBAAkB,EAAE,CAAC;gBACrC,iBAAiB,GAAG,IAAI,CAAC;gBACzB,MAAM,UAAU,GAAG,kBAAkB,CACnC,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,SAAS,CACd,CAAC;gBAEF,OAAO,CAAC,IAAI,CAAC;oBACX,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI;oBAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO;oBACjC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI;oBAC1B,eAAe,EAAE,GAAG,CAAC,MAAM;oBAC3B,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,EAAE;oBACpD,UAAU;iBACX,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACpC,IAAI,OAAO,GAAG,iCAAiC,CAAC;IAEhD,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,iBAAiB,EAAE,CAAC;YACtB,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;YAC1E,OAAO,GAAG,6BAA6B,OAAO,CAAC,MAAM,uBAAuB,kBAAkB,oBAAoB,aAAa,IAAI,CAAC;QACtI,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,GAAG,OAAO,CAAC,MAAM,sCAAsC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ;QACR,eAAe,EAAE;YACf,WAAW;YACX,gBAAgB;YAChB,UAAU;YACV,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC9B,iBAAiB;SAClB;QACD,OAAO;QACP,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Safety Detection Types
|
|
3
|
+
*
|
|
4
|
+
* These detect LLM-specific antipatterns in commit history:
|
|
5
|
+
* 1. Secret Leakage - Exposed API keys, tokens, credentials
|
|
6
|
+
* 2. Scope Violation - Commits touching files outside declared domain
|
|
7
|
+
* 3. Contract Drift - Commit message format degrading over time
|
|
8
|
+
* 4. Token Spiral - Runaway token consumption (context explosion)
|
|
9
|
+
*/
|
|
10
|
+
export interface SecretLeakageResult {
|
|
11
|
+
detected: boolean;
|
|
12
|
+
findings: SecretFinding[];
|
|
13
|
+
totalFindings: number;
|
|
14
|
+
criticalFindings: number;
|
|
15
|
+
message: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SecretFinding {
|
|
18
|
+
commitHash: string;
|
|
19
|
+
commitMessage: string;
|
|
20
|
+
timestamp: Date;
|
|
21
|
+
file: string;
|
|
22
|
+
pattern: string;
|
|
23
|
+
matchedText: string;
|
|
24
|
+
lineContext: string;
|
|
25
|
+
severity: 'critical' | 'warning';
|
|
26
|
+
}
|
|
27
|
+
export declare const SECRET_PATTERNS: Record<string, RegExp>;
|
|
28
|
+
export interface ScopeViolationResult {
|
|
29
|
+
detected: boolean;
|
|
30
|
+
violations: ScopeViolation[];
|
|
31
|
+
totalViolations: number;
|
|
32
|
+
totalUnauthorizedFiles: number;
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ScopeViolation {
|
|
36
|
+
commitHash: string;
|
|
37
|
+
commitMessage: string;
|
|
38
|
+
timestamp: Date;
|
|
39
|
+
unauthorizedFiles: string[];
|
|
40
|
+
declaredScope: string[];
|
|
41
|
+
description: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ScopeDefinition {
|
|
44
|
+
allowedPatterns: string[];
|
|
45
|
+
allowedDirs: string[];
|
|
46
|
+
description?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ContractDriftResult {
|
|
49
|
+
detected: boolean;
|
|
50
|
+
driftMetrics: ContractDriftMetrics;
|
|
51
|
+
degradingCommits: DegradingCommit[];
|
|
52
|
+
totalDegradingCommits: number;
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
export interface ContractDriftMetrics {
|
|
56
|
+
baselineCompliance: number;
|
|
57
|
+
currentCompliance: number;
|
|
58
|
+
driftPercentage: number;
|
|
59
|
+
trend: 'improving' | 'stable' | 'degrading';
|
|
60
|
+
entropyScore: number;
|
|
61
|
+
}
|
|
62
|
+
export interface DegradingCommit {
|
|
63
|
+
hash: string;
|
|
64
|
+
message: string;
|
|
65
|
+
timestamp: Date;
|
|
66
|
+
issues: string[];
|
|
67
|
+
complianceScore: number;
|
|
68
|
+
}
|
|
69
|
+
export interface TokenSpiralResult {
|
|
70
|
+
detected: boolean;
|
|
71
|
+
estimatedTokens: TokenEstimate;
|
|
72
|
+
spirals: TokenSpiral[];
|
|
73
|
+
message: string;
|
|
74
|
+
}
|
|
75
|
+
export interface TokenEstimate {
|
|
76
|
+
totalTokens: number;
|
|
77
|
+
averagePerCommit: number;
|
|
78
|
+
peakCommit: {
|
|
79
|
+
hash: string;
|
|
80
|
+
tokens: number;
|
|
81
|
+
message: string;
|
|
82
|
+
} | null;
|
|
83
|
+
baseline: number;
|
|
84
|
+
explosionDetected: boolean;
|
|
85
|
+
}
|
|
86
|
+
export interface TokenSpiral {
|
|
87
|
+
commitHash: string;
|
|
88
|
+
commitMessage: string;
|
|
89
|
+
timestamp: Date;
|
|
90
|
+
estimatedTokens: number;
|
|
91
|
+
baselineMultiplier: number;
|
|
92
|
+
indicators: string[];
|
|
93
|
+
}
|
|
94
|
+
export interface AISafetyAnalysis {
|
|
95
|
+
secretLeakage: SecretLeakageResult;
|
|
96
|
+
scopeViolations: ScopeViolationResult;
|
|
97
|
+
contractDrift: ContractDriftResult;
|
|
98
|
+
tokenSpiral: TokenSpiralResult;
|
|
99
|
+
summary: {
|
|
100
|
+
totalIssues: number;
|
|
101
|
+
criticalIssues: number;
|
|
102
|
+
warningIssues: number;
|
|
103
|
+
overallHealth: 'healthy' | 'warning' | 'critical';
|
|
104
|
+
};
|
|
105
|
+
recommendations: string[];
|
|
106
|
+
}
|
|
107
|
+
export interface AISafetyConfig {
|
|
108
|
+
secretDetectionEnabled: boolean;
|
|
109
|
+
customSecretPatterns?: Record<string, RegExp>;
|
|
110
|
+
secretScanDepth: 'full' | 'shallow';
|
|
111
|
+
scopeCheckEnabled: boolean;
|
|
112
|
+
scope?: ScopeDefinition;
|
|
113
|
+
allowedExceptions: string[];
|
|
114
|
+
contractCheckEnabled: boolean;
|
|
115
|
+
driftThreshold: number;
|
|
116
|
+
requireConventionalCommits: boolean;
|
|
117
|
+
tokenTrackingEnabled: boolean;
|
|
118
|
+
tokenEstimationMethod: 'char_count' | 'lines' | 'diff_size';
|
|
119
|
+
explosionThreshold: number;
|
|
120
|
+
}
|
|
121
|
+
export declare const DEFAULT_AI_SAFETY_CONFIG: AISafetyConfig;
|
|
122
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ai-safety/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Safety Detection Types
|
|
3
|
+
*
|
|
4
|
+
* These detect LLM-specific antipatterns in commit history:
|
|
5
|
+
* 1. Secret Leakage - Exposed API keys, tokens, credentials
|
|
6
|
+
* 2. Scope Violation - Commits touching files outside declared domain
|
|
7
|
+
* 3. Contract Drift - Commit message format degrading over time
|
|
8
|
+
* 4. Token Spiral - Runaway token consumption (context explosion)
|
|
9
|
+
*/
|
|
10
|
+
export const SECRET_PATTERNS = {
|
|
11
|
+
'OpenAI API Key': /sk-[a-zA-Z0-9]{48}/g,
|
|
12
|
+
'GitHub Personal Access Token': /ghp_[a-zA-Z0-9]{36}/g,
|
|
13
|
+
'GitLab Personal Access Token': /glpat-[a-zA-Z0-9]{20}/g,
|
|
14
|
+
'AWS Access Key': /AKIA[0-9A-Z]{16}/g,
|
|
15
|
+
'Slack Token': /xox[baprs]-[a-zA-Z0-9-]+/g,
|
|
16
|
+
'Generic API Key': /api[_-]?key\s*[:=]\s*['"]?[a-zA-Z0-9]{32,}['"]?/gi,
|
|
17
|
+
'Generic Secret': /secret\s*[:=]\s*['"]?[a-zA-Z0-9]{16,}['"]?/gi,
|
|
18
|
+
'Password Assignment': /password\s*[:=]\s*['"]?[^\s'"]{8,}['"]?/gi,
|
|
19
|
+
};
|
|
20
|
+
export const DEFAULT_AI_SAFETY_CONFIG = {
|
|
21
|
+
secretDetectionEnabled: true,
|
|
22
|
+
secretScanDepth: 'full',
|
|
23
|
+
scopeCheckEnabled: false, // Requires explicit scope definition
|
|
24
|
+
allowedExceptions: ['package.json', 'package-lock.json', 'tsconfig.json', '.gitignore', 'README.md'],
|
|
25
|
+
contractCheckEnabled: true,
|
|
26
|
+
driftThreshold: 30,
|
|
27
|
+
requireConventionalCommits: true,
|
|
28
|
+
tokenTrackingEnabled: true,
|
|
29
|
+
tokenEstimationMethod: 'char_count',
|
|
30
|
+
explosionThreshold: 2.0,
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/ai-safety/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2BH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,gBAAgB,EAAE,qBAAqB;IACvC,8BAA8B,EAAE,sBAAsB;IACtD,8BAA8B,EAAE,wBAAwB;IACxD,gBAAgB,EAAE,mBAAmB;IACrC,aAAa,EAAE,2BAA2B;IAC1C,iBAAiB,EAAE,mDAAmD;IACtE,gBAAgB,EAAE,8CAA8C;IAChE,qBAAqB,EAAE,2CAA2C;CACnE,CAAC;AA0IF,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACtD,sBAAsB,EAAE,IAAI;IAC5B,eAAe,EAAE,MAAM;IACvB,iBAAiB,EAAE,KAAK,EAAE,qCAAqC;IAC/D,iBAAiB,EAAE,CAAC,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,CAAC;IACpG,oBAAoB,EAAE,IAAI;IAC1B,cAAc,EAAE,EAAE;IAClB,0BAA0B,EAAE,IAAI;IAChC,oBAAoB,EAAE,IAAI;IAC1B,qBAAqB,EAAE,YAAY;IACnC,kBAAkB,EAAE,GAAG;CACxB,CAAC"}
|