@rigour-labs/core 2.21.2 → 3.0.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/README.md +58 -0
- package/dist/context.test.js +2 -3
- package/dist/environment.test.js +2 -1
- package/dist/gates/agent-team.d.ts +2 -1
- package/dist/gates/agent-team.js +1 -0
- package/dist/gates/base.d.ts +4 -2
- package/dist/gates/base.js +5 -1
- package/dist/gates/checkpoint.d.ts +2 -1
- package/dist/gates/checkpoint.js +3 -2
- package/dist/gates/content.js +1 -1
- package/dist/gates/context-window-artifacts.d.ts +34 -0
- package/dist/gates/context-window-artifacts.js +214 -0
- package/dist/gates/context.d.ts +2 -1
- package/dist/gates/context.js +4 -3
- package/dist/gates/coverage.js +3 -1
- package/dist/gates/dependency.js +5 -5
- package/dist/gates/duplication-drift.d.ts +33 -0
- package/dist/gates/duplication-drift.js +190 -0
- package/dist/gates/environment.js +4 -4
- package/dist/gates/file.js +1 -1
- package/dist/gates/hallucinated-imports.d.ts +63 -0
- package/dist/gates/hallucinated-imports.js +406 -0
- package/dist/gates/inconsistent-error-handling.d.ts +39 -0
- package/dist/gates/inconsistent-error-handling.js +236 -0
- package/dist/gates/promise-safety.d.ts +68 -0
- package/dist/gates/promise-safety.js +509 -0
- package/dist/gates/retry-loop-breaker.d.ts +2 -1
- package/dist/gates/retry-loop-breaker.js +2 -1
- package/dist/gates/runner.js +62 -1
- package/dist/gates/safety.d.ts +2 -1
- package/dist/gates/safety.js +2 -1
- package/dist/gates/security-patterns.d.ts +2 -1
- package/dist/gates/security-patterns.js +2 -1
- package/dist/gates/structure.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/services/fix-packet-service.d.ts +0 -1
- package/dist/services/fix-packet-service.js +9 -14
- package/dist/services/score-history.d.ts +54 -0
- package/dist/services/score-history.js +122 -0
- package/dist/templates/index.js +195 -0
- package/dist/types/fix-packet.d.ts +5 -5
- package/dist/types/fix-packet.js +1 -1
- package/dist/types/index.d.ts +430 -0
- package/dist/types/index.js +57 -0
- package/package.json +21 -1
- package/src/context.test.ts +0 -256
- package/src/discovery.test.ts +0 -88
- package/src/discovery.ts +0 -112
- package/src/environment.test.ts +0 -115
- package/src/gates/agent-team.test.ts +0 -134
- package/src/gates/agent-team.ts +0 -210
- package/src/gates/ast-handlers/base.ts +0 -13
- package/src/gates/ast-handlers/python.ts +0 -145
- package/src/gates/ast-handlers/python_parser.py +0 -181
- package/src/gates/ast-handlers/typescript.ts +0 -264
- package/src/gates/ast-handlers/universal.ts +0 -184
- package/src/gates/ast.ts +0 -54
- package/src/gates/base.ts +0 -27
- package/src/gates/checkpoint.test.ts +0 -135
- package/src/gates/checkpoint.ts +0 -311
- package/src/gates/content.ts +0 -50
- package/src/gates/context.ts +0 -267
- package/src/gates/coverage.ts +0 -74
- package/src/gates/dependency.ts +0 -108
- package/src/gates/environment.ts +0 -94
- package/src/gates/file.ts +0 -42
- package/src/gates/retry-loop-breaker.ts +0 -151
- package/src/gates/runner.ts +0 -156
- package/src/gates/safety.ts +0 -56
- package/src/gates/security-patterns.test.ts +0 -162
- package/src/gates/security-patterns.ts +0 -305
- package/src/gates/structure.ts +0 -36
- package/src/index.ts +0 -13
- package/src/pattern-index/embeddings.ts +0 -84
- package/src/pattern-index/index.ts +0 -59
- package/src/pattern-index/indexer.test.ts +0 -276
- package/src/pattern-index/indexer.ts +0 -1023
- package/src/pattern-index/matcher.test.ts +0 -293
- package/src/pattern-index/matcher.ts +0 -493
- package/src/pattern-index/overrides.ts +0 -235
- package/src/pattern-index/security.ts +0 -151
- package/src/pattern-index/staleness.test.ts +0 -313
- package/src/pattern-index/staleness.ts +0 -568
- package/src/pattern-index/types.ts +0 -339
- package/src/safety.test.ts +0 -53
- package/src/services/adaptive-thresholds.test.ts +0 -189
- package/src/services/adaptive-thresholds.ts +0 -275
- package/src/services/context-engine.ts +0 -104
- package/src/services/fix-packet-service.ts +0 -42
- package/src/services/state-service.ts +0 -138
- package/src/smoke.test.ts +0 -18
- package/src/templates/index.ts +0 -312
- package/src/types/fix-packet.ts +0 -32
- package/src/types/index.ts +0 -159
- package/src/utils/logger.ts +0 -43
- package/src/utils/scanner.test.ts +0 -37
- package/src/utils/scanner.ts +0 -43
- package/tsconfig.json +0 -10
- package/vitest.config.ts +0 -7
- package/vitest.setup.ts +0 -30
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { SecurityPatternsGate, checkSecurityPatterns } from './security-patterns.js';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import * as os from 'os';
|
|
6
|
-
|
|
7
|
-
describe('SecurityPatternsGate', () => {
|
|
8
|
-
let testDir: string;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'security-test-'));
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('gate initialization', () => {
|
|
19
|
-
it('should create gate with default config', () => {
|
|
20
|
-
const gate = new SecurityPatternsGate();
|
|
21
|
-
expect(gate.id).toBe('security-patterns');
|
|
22
|
-
expect(gate.title).toBe('Security Pattern Detection');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should skip when not enabled', async () => {
|
|
26
|
-
const gate = new SecurityPatternsGate({ enabled: false });
|
|
27
|
-
const failures = await gate.run({ cwd: testDir });
|
|
28
|
-
expect(failures).toEqual([]);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('SQL injection detection', () => {
|
|
33
|
-
it('should detect string concatenation in queries', async () => {
|
|
34
|
-
const filePath = path.join(testDir, 'db.ts');
|
|
35
|
-
fs.writeFileSync(filePath, `
|
|
36
|
-
const userId = req.params.id;
|
|
37
|
-
db.query("SELECT * FROM users WHERE id = " + userId + " LIMIT 1");
|
|
38
|
-
`);
|
|
39
|
-
|
|
40
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
41
|
-
expect(vulns.some(v => v.type === 'sql_injection')).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should detect template literal SQL', async () => {
|
|
45
|
-
const filePath = path.join(testDir, 'query.ts');
|
|
46
|
-
fs.writeFileSync(filePath, `
|
|
47
|
-
db.execute(\`SELECT * FROM users WHERE id = \${userId}\`);
|
|
48
|
-
`);
|
|
49
|
-
|
|
50
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
51
|
-
expect(vulns.some(v => v.type === 'sql_injection')).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe('XSS detection', () => {
|
|
56
|
-
it('should detect innerHTML assignment', async () => {
|
|
57
|
-
const filePath = path.join(testDir, 'dom.js');
|
|
58
|
-
fs.writeFileSync(filePath, `
|
|
59
|
-
element.innerHTML = userInput;
|
|
60
|
-
`);
|
|
61
|
-
|
|
62
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
63
|
-
expect(vulns.some(v => v.type === 'xss')).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should detect dangerouslySetInnerHTML', async () => {
|
|
67
|
-
const filePath = path.join(testDir, 'component.tsx');
|
|
68
|
-
fs.writeFileSync(filePath, `
|
|
69
|
-
<div dangerouslySetInnerHTML={{ __html: content }} />
|
|
70
|
-
`);
|
|
71
|
-
|
|
72
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
73
|
-
expect(vulns.some(v => v.type === 'xss')).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('hardcoded secrets detection', () => {
|
|
78
|
-
it('should detect hardcoded API keys', async () => {
|
|
79
|
-
const filePath = path.join(testDir, 'config.ts');
|
|
80
|
-
fs.writeFileSync(filePath, `
|
|
81
|
-
const API_KEY = "sk-1234567890abcdefghijklmnopqrst";
|
|
82
|
-
`);
|
|
83
|
-
|
|
84
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
85
|
-
expect(vulns.some(v => v.type === 'hardcoded_secrets')).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should detect private keys', async () => {
|
|
89
|
-
const filePath = path.join(testDir, 'key.ts');
|
|
90
|
-
fs.writeFileSync(filePath, `
|
|
91
|
-
const privateKey = "-----BEGIN RSA PRIVATE KEY-----";
|
|
92
|
-
`);
|
|
93
|
-
|
|
94
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
95
|
-
expect(vulns.some(v => v.type === 'hardcoded_secrets')).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should detect password assignments', async () => {
|
|
99
|
-
const filePath = path.join(testDir, 'auth.ts');
|
|
100
|
-
fs.writeFileSync(filePath, `
|
|
101
|
-
const password = "supersecretpassword123";
|
|
102
|
-
`);
|
|
103
|
-
|
|
104
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
105
|
-
expect(vulns.some(v => v.type === 'hardcoded_secrets')).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe('insecure randomness detection', () => {
|
|
110
|
-
it('should detect Math.random usage', async () => {
|
|
111
|
-
const filePath = path.join(testDir, 'token.ts');
|
|
112
|
-
fs.writeFileSync(filePath, `
|
|
113
|
-
const token = Math.random().toString(36);
|
|
114
|
-
`);
|
|
115
|
-
|
|
116
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
117
|
-
expect(vulns.some(v => v.type === 'insecure_randomness')).toBe(true);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('command injection detection', () => {
|
|
122
|
-
it('should detect exec with user input', async () => {
|
|
123
|
-
const filePath = path.join(testDir, 'shell.ts');
|
|
124
|
-
fs.writeFileSync(filePath, `
|
|
125
|
-
exec(\`ls \${req.query.path}\`);
|
|
126
|
-
`);
|
|
127
|
-
|
|
128
|
-
const vulns = await checkSecurityPatterns(filePath);
|
|
129
|
-
expect(vulns.some(v => v.type === 'command_injection')).toBe(true);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('severity filtering', () => {
|
|
134
|
-
it('should block on high severity by default', async () => {
|
|
135
|
-
const gate = new SecurityPatternsGate({ enabled: true });
|
|
136
|
-
|
|
137
|
-
const filePath = path.join(testDir, 'test.ts');
|
|
138
|
-
fs.writeFileSync(filePath, `
|
|
139
|
-
element.innerHTML = userInput;
|
|
140
|
-
`);
|
|
141
|
-
|
|
142
|
-
const failures = await gate.run({ cwd: testDir });
|
|
143
|
-
expect(failures.length).toBeGreaterThan(0);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should respect block_on_severity threshold', async () => {
|
|
147
|
-
const gate = new SecurityPatternsGate({
|
|
148
|
-
enabled: true,
|
|
149
|
-
block_on_severity: 'critical'
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// innerHTML is 'high', not 'critical'
|
|
153
|
-
const filePath = path.join(testDir, 'test.ts');
|
|
154
|
-
fs.writeFileSync(filePath, `
|
|
155
|
-
element.innerHTML = userInput;
|
|
156
|
-
`);
|
|
157
|
-
|
|
158
|
-
const failures = await gate.run({ cwd: testDir });
|
|
159
|
-
expect(failures).toHaveLength(0); // High severity not blocked
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
});
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Security Patterns Gate
|
|
3
|
-
*
|
|
4
|
-
* Detects code-level security vulnerabilities for frontier models
|
|
5
|
-
* that may generate insecure patterns at scale.
|
|
6
|
-
*
|
|
7
|
-
* Patterns covered:
|
|
8
|
-
* - SQL Injection
|
|
9
|
-
* - XSS (Cross-Site Scripting)
|
|
10
|
-
* - Path Traversal
|
|
11
|
-
* - Hardcoded Secrets
|
|
12
|
-
* - Insecure Randomness
|
|
13
|
-
* - Command Injection
|
|
14
|
-
*
|
|
15
|
-
* @since v2.14.0
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { Gate, GateContext } from './base.js';
|
|
19
|
-
import { Failure } from '../types/index.js';
|
|
20
|
-
import { FileScanner } from '../utils/scanner.js';
|
|
21
|
-
import { Logger } from '../utils/logger.js';
|
|
22
|
-
import fs from 'fs-extra';
|
|
23
|
-
import path from 'path';
|
|
24
|
-
|
|
25
|
-
export interface SecurityVulnerability {
|
|
26
|
-
type: string;
|
|
27
|
-
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
28
|
-
file: string;
|
|
29
|
-
line: number;
|
|
30
|
-
match: string;
|
|
31
|
-
description: string;
|
|
32
|
-
cwe?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface SecurityPatternsConfig {
|
|
36
|
-
enabled?: boolean;
|
|
37
|
-
sql_injection?: boolean;
|
|
38
|
-
xss?: boolean;
|
|
39
|
-
path_traversal?: boolean;
|
|
40
|
-
hardcoded_secrets?: boolean;
|
|
41
|
-
insecure_randomness?: boolean;
|
|
42
|
-
command_injection?: boolean;
|
|
43
|
-
block_on_severity?: 'critical' | 'high' | 'medium' | 'low';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Pattern definitions with regex and metadata
|
|
47
|
-
const VULNERABILITY_PATTERNS: {
|
|
48
|
-
type: string;
|
|
49
|
-
regex: RegExp;
|
|
50
|
-
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
51
|
-
description: string;
|
|
52
|
-
cwe: string;
|
|
53
|
-
languages: string[];
|
|
54
|
-
}[] = [
|
|
55
|
-
// SQL Injection
|
|
56
|
-
{
|
|
57
|
-
type: 'sql_injection',
|
|
58
|
-
regex: /(?:execute|query|raw|exec)\s*\(\s*[`'"].*\$\{.+\}|`\s*\+\s*\w+|\$\{.+\}.*(?:SELECT|INSERT|UPDATE|DELETE|DROP)/gi,
|
|
59
|
-
severity: 'critical',
|
|
60
|
-
description: 'Potential SQL injection: User input concatenated into SQL query',
|
|
61
|
-
cwe: 'CWE-89',
|
|
62
|
-
languages: ['ts', 'js', 'py']
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
type: 'sql_injection',
|
|
66
|
-
regex: /\.query\s*\(\s*['"`].*\+.*\+.*['"`]\s*\)/g,
|
|
67
|
-
severity: 'critical',
|
|
68
|
-
description: 'SQL query built with string concatenation',
|
|
69
|
-
cwe: 'CWE-89',
|
|
70
|
-
languages: ['ts', 'js']
|
|
71
|
-
},
|
|
72
|
-
// XSS
|
|
73
|
-
{
|
|
74
|
-
type: 'xss',
|
|
75
|
-
regex: /innerHTML\s*=\s*(?!\s*['"`]\s*['"`])[^;]+/g,
|
|
76
|
-
severity: 'high',
|
|
77
|
-
description: 'Potential XSS: innerHTML assignment with dynamic content',
|
|
78
|
-
cwe: 'CWE-79',
|
|
79
|
-
languages: ['ts', 'js', 'tsx', 'jsx']
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
type: 'xss',
|
|
83
|
-
regex: /dangerouslySetInnerHTML\s*=\s*\{/g,
|
|
84
|
-
severity: 'high',
|
|
85
|
-
description: 'dangerouslySetInnerHTML usage (ensure content is sanitized)',
|
|
86
|
-
cwe: 'CWE-79',
|
|
87
|
-
languages: ['tsx', 'jsx']
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
type: 'xss',
|
|
91
|
-
regex: /document\.write\s*\(/g,
|
|
92
|
-
severity: 'high',
|
|
93
|
-
description: 'document.write is dangerous for XSS',
|
|
94
|
-
cwe: 'CWE-79',
|
|
95
|
-
languages: ['ts', 'js']
|
|
96
|
-
},
|
|
97
|
-
// Path Traversal
|
|
98
|
-
{
|
|
99
|
-
type: 'path_traversal',
|
|
100
|
-
regex: /(?:readFile|writeFile|readdir|unlink|rmdir)\s*\([^)]*(?:req\.(?:params|query|body)|\.\.\/)/g,
|
|
101
|
-
severity: 'high',
|
|
102
|
-
description: 'Potential path traversal: File operation with user input',
|
|
103
|
-
cwe: 'CWE-22',
|
|
104
|
-
languages: ['ts', 'js']
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
type: 'path_traversal',
|
|
108
|
-
regex: /path\.join\s*\([^)]*req\./g,
|
|
109
|
-
severity: 'medium',
|
|
110
|
-
description: 'path.join with request data (verify input sanitization)',
|
|
111
|
-
cwe: 'CWE-22',
|
|
112
|
-
languages: ['ts', 'js']
|
|
113
|
-
},
|
|
114
|
-
// Hardcoded Secrets
|
|
115
|
-
{
|
|
116
|
-
type: 'hardcoded_secrets',
|
|
117
|
-
regex: /(?:password|secret|api_key|apikey|auth_token|access_token|private_key)\s*[:=]\s*['"][^'"]{8,}['"]/gi,
|
|
118
|
-
severity: 'critical',
|
|
119
|
-
description: 'Hardcoded secret detected in code',
|
|
120
|
-
cwe: 'CWE-798',
|
|
121
|
-
languages: ['ts', 'js', 'py', 'java', 'go']
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
type: 'hardcoded_secrets',
|
|
125
|
-
regex: /(?:sk-|pk-|rk-|ghp_|gho_|ghu_|ghs_|ghr_)[a-zA-Z0-9]{20,}/g,
|
|
126
|
-
severity: 'critical',
|
|
127
|
-
description: 'API key pattern detected (OpenAI, GitHub, etc.)',
|
|
128
|
-
cwe: 'CWE-798',
|
|
129
|
-
languages: ['*']
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
type: 'hardcoded_secrets',
|
|
133
|
-
regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
|
|
134
|
-
severity: 'critical',
|
|
135
|
-
description: 'Private key embedded in source code',
|
|
136
|
-
cwe: 'CWE-798',
|
|
137
|
-
languages: ['*']
|
|
138
|
-
},
|
|
139
|
-
// Insecure Randomness
|
|
140
|
-
{
|
|
141
|
-
type: 'insecure_randomness',
|
|
142
|
-
regex: /Math\.random\s*\(\s*\)/g,
|
|
143
|
-
severity: 'medium',
|
|
144
|
-
description: 'Math.random() is not cryptographically secure',
|
|
145
|
-
cwe: 'CWE-338',
|
|
146
|
-
languages: ['ts', 'js', 'tsx', 'jsx']
|
|
147
|
-
},
|
|
148
|
-
// Command Injection
|
|
149
|
-
{
|
|
150
|
-
type: 'command_injection',
|
|
151
|
-
regex: /(?:exec|spawn|execSync|spawnSync)\s*\([^)]*(?:req\.|`.*\$\{)/g,
|
|
152
|
-
severity: 'critical',
|
|
153
|
-
description: 'Potential command injection: shell execution with user input',
|
|
154
|
-
cwe: 'CWE-78',
|
|
155
|
-
languages: ['ts', 'js']
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
type: 'command_injection',
|
|
159
|
-
regex: /child_process.*\s*\.\s*(?:exec|spawn)\s*\(/g,
|
|
160
|
-
severity: 'high',
|
|
161
|
-
description: 'child_process usage detected (verify input sanitization)',
|
|
162
|
-
cwe: 'CWE-78',
|
|
163
|
-
languages: ['ts', 'js']
|
|
164
|
-
},
|
|
165
|
-
];
|
|
166
|
-
|
|
167
|
-
export class SecurityPatternsGate extends Gate {
|
|
168
|
-
private config: SecurityPatternsConfig;
|
|
169
|
-
private severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
170
|
-
|
|
171
|
-
constructor(config: SecurityPatternsConfig = {}) {
|
|
172
|
-
super('security-patterns', 'Security Pattern Detection');
|
|
173
|
-
this.config = {
|
|
174
|
-
enabled: config.enabled ?? true,
|
|
175
|
-
sql_injection: config.sql_injection ?? true,
|
|
176
|
-
xss: config.xss ?? true,
|
|
177
|
-
path_traversal: config.path_traversal ?? true,
|
|
178
|
-
hardcoded_secrets: config.hardcoded_secrets ?? true,
|
|
179
|
-
insecure_randomness: config.insecure_randomness ?? true,
|
|
180
|
-
command_injection: config.command_injection ?? true,
|
|
181
|
-
block_on_severity: config.block_on_severity ?? 'high',
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async run(context: GateContext): Promise<Failure[]> {
|
|
186
|
-
if (!this.config.enabled) {
|
|
187
|
-
return [];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const failures: Failure[] = [];
|
|
191
|
-
const vulnerabilities: SecurityVulnerability[] = [];
|
|
192
|
-
|
|
193
|
-
const files = await FileScanner.findFiles({
|
|
194
|
-
cwd: context.cwd,
|
|
195
|
-
patterns: ['**/*.{ts,js,tsx,jsx,py,java,go}'],
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
Logger.info(`Security Patterns Gate: Scanning ${files.length} files`);
|
|
199
|
-
|
|
200
|
-
for (const file of files) {
|
|
201
|
-
try {
|
|
202
|
-
const fullPath = path.join(context.cwd, file);
|
|
203
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
204
|
-
const ext = path.extname(file).slice(1);
|
|
205
|
-
|
|
206
|
-
this.scanFileForVulnerabilities(content, file, ext, vulnerabilities);
|
|
207
|
-
} catch (e) { }
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Filter by enabled checks
|
|
211
|
-
const filteredVulns = vulnerabilities.filter(v => {
|
|
212
|
-
switch (v.type) {
|
|
213
|
-
case 'sql_injection': return this.config.sql_injection;
|
|
214
|
-
case 'xss': return this.config.xss;
|
|
215
|
-
case 'path_traversal': return this.config.path_traversal;
|
|
216
|
-
case 'hardcoded_secrets': return this.config.hardcoded_secrets;
|
|
217
|
-
case 'insecure_randomness': return this.config.insecure_randomness;
|
|
218
|
-
case 'command_injection': return this.config.command_injection;
|
|
219
|
-
default: return true;
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// Sort by severity
|
|
224
|
-
filteredVulns.sort((a, b) =>
|
|
225
|
-
this.severityOrder[a.severity] - this.severityOrder[b.severity]
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
// Convert to failures based on block_on_severity threshold
|
|
229
|
-
const blockThreshold = this.severityOrder[this.config.block_on_severity ?? 'high'];
|
|
230
|
-
|
|
231
|
-
for (const vuln of filteredVulns) {
|
|
232
|
-
if (this.severityOrder[vuln.severity] <= blockThreshold) {
|
|
233
|
-
failures.push(this.createFailure(
|
|
234
|
-
`[${vuln.cwe}] ${vuln.description}`,
|
|
235
|
-
[vuln.file],
|
|
236
|
-
`Found: "${vuln.match.slice(0, 60)}..." - Use parameterized queries/sanitization.`,
|
|
237
|
-
`Security: ${vuln.type.replace('_', ' ').toUpperCase()}`,
|
|
238
|
-
vuln.line,
|
|
239
|
-
vuln.line
|
|
240
|
-
));
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (filteredVulns.length > 0 && failures.length === 0) {
|
|
245
|
-
// Vulnerabilities found but below threshold - log info
|
|
246
|
-
Logger.info(`Security scan found ${filteredVulns.length} issues below ${this.config.block_on_severity} threshold`);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return failures;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
private scanFileForVulnerabilities(
|
|
253
|
-
content: string,
|
|
254
|
-
file: string,
|
|
255
|
-
ext: string,
|
|
256
|
-
vulnerabilities: SecurityVulnerability[]
|
|
257
|
-
): void {
|
|
258
|
-
const lines = content.split('\n');
|
|
259
|
-
|
|
260
|
-
for (const pattern of VULNERABILITY_PATTERNS) {
|
|
261
|
-
// Check if pattern applies to this file type
|
|
262
|
-
if (!pattern.languages.includes('*') && !pattern.languages.includes(ext)) {
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Reset regex state
|
|
267
|
-
pattern.regex.lastIndex = 0;
|
|
268
|
-
|
|
269
|
-
let match;
|
|
270
|
-
while ((match = pattern.regex.exec(content)) !== null) {
|
|
271
|
-
// Find line number
|
|
272
|
-
const beforeMatch = content.slice(0, match.index);
|
|
273
|
-
const lineNumber = beforeMatch.split('\n').length;
|
|
274
|
-
|
|
275
|
-
vulnerabilities.push({
|
|
276
|
-
type: pattern.type,
|
|
277
|
-
severity: pattern.severity,
|
|
278
|
-
file,
|
|
279
|
-
line: lineNumber,
|
|
280
|
-
match: match[0],
|
|
281
|
-
description: pattern.description,
|
|
282
|
-
cwe: pattern.cwe,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Quick helper to check a single file for security issues
|
|
291
|
-
*/
|
|
292
|
-
export async function checkSecurityPatterns(
|
|
293
|
-
filePath: string,
|
|
294
|
-
config: SecurityPatternsConfig = { enabled: true }
|
|
295
|
-
): Promise<SecurityVulnerability[]> {
|
|
296
|
-
const gate = new SecurityPatternsGate(config);
|
|
297
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
298
|
-
const ext = path.extname(filePath).slice(1);
|
|
299
|
-
const vulnerabilities: SecurityVulnerability[] = [];
|
|
300
|
-
|
|
301
|
-
// Use the private method via reflection for testing
|
|
302
|
-
(gate as any).scanFileForVulnerabilities(content, filePath, ext, vulnerabilities);
|
|
303
|
-
|
|
304
|
-
return vulnerabilities;
|
|
305
|
-
}
|
package/src/gates/structure.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { Gate, GateContext } from './base.js';
|
|
4
|
-
import { Failure } from '../types/index.js';
|
|
5
|
-
|
|
6
|
-
export interface StructureGateConfig {
|
|
7
|
-
requiredFiles: string[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class StructureGate extends Gate {
|
|
11
|
-
constructor(private config: StructureGateConfig) {
|
|
12
|
-
super('structure-check', 'Project Structure');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async run(context: GateContext): Promise<Failure[]> {
|
|
16
|
-
const missing: string[] = [];
|
|
17
|
-
for (const file of this.config.requiredFiles) {
|
|
18
|
-
const filePath = path.join(context.cwd, file);
|
|
19
|
-
if (!(await fs.pathExists(filePath))) {
|
|
20
|
-
missing.push(file);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (missing.length > 0) {
|
|
25
|
-
return [
|
|
26
|
-
this.createFailure(
|
|
27
|
-
'The following required files are missing:',
|
|
28
|
-
missing,
|
|
29
|
-
'Create these files to maintain project documentation and consistency.'
|
|
30
|
-
),
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export * from './types/index.js';
|
|
2
|
-
export * from './gates/runner.js';
|
|
3
|
-
export * from './discovery.js';
|
|
4
|
-
export * from './services/fix-packet-service.js';
|
|
5
|
-
export * from './templates/index.js';
|
|
6
|
-
export * from './types/fix-packet.js';
|
|
7
|
-
export { Gate, GateContext } from './gates/base.js';
|
|
8
|
-
export { RetryLoopBreakerGate } from './gates/retry-loop-breaker.js';
|
|
9
|
-
export * from './utils/logger.js';
|
|
10
|
-
// Pattern Index is intentionally NOT exported here to prevent
|
|
11
|
-
// native dependency issues (sharp/transformers) from leaking into
|
|
12
|
-
// non-AI parts of the system.
|
|
13
|
-
// Import from @rigour-labs/core/pattern-index instead.
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic Embedding Service
|
|
3
|
-
*
|
|
4
|
-
* Uses Transformers.js for local vector embeddings.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Singleton for the embedding pipeline to avoid re-loading the model.
|
|
9
|
-
*/
|
|
10
|
-
let embeddingPipeline: any = null;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get or initialize the embedding pipeline.
|
|
14
|
-
*/
|
|
15
|
-
async function getPipeline() {
|
|
16
|
-
// Definitive bypass for tests to avoid native 'sharp' dependency issues
|
|
17
|
-
if (process.env.VITEST) {
|
|
18
|
-
return async (text: string) => {
|
|
19
|
-
const vector = new Array(384).fill(0);
|
|
20
|
-
for (let i = 0; i < Math.min(text.length, 384); i++) {
|
|
21
|
-
vector[i] = text.charCodeAt(i) / 255;
|
|
22
|
-
}
|
|
23
|
-
return { data: new Float32Array(vector) };
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!embeddingPipeline) {
|
|
28
|
-
try {
|
|
29
|
-
// Dynamic import to isolate native dependency issues (like sharp)
|
|
30
|
-
const { pipeline } = await import('@xenova/transformers');
|
|
31
|
-
|
|
32
|
-
// Using a compact but high-quality model for local embeddings
|
|
33
|
-
embeddingPipeline = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
34
|
-
} catch (error) {
|
|
35
|
-
console.error('Failed to initialize embedding pipeline:', error);
|
|
36
|
-
throw error;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return embeddingPipeline;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Generate an embedding for a piece of text.
|
|
44
|
-
*/
|
|
45
|
-
export async function generateEmbedding(text: string): Promise<number[]> {
|
|
46
|
-
try {
|
|
47
|
-
const extractor = await getPipeline();
|
|
48
|
-
const output = await extractor(text, { pooling: 'mean', normalize: true });
|
|
49
|
-
return Array.from(output.data);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.warn('Semantic reasoning disabled: Embedding generation failed.', error);
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Calculate cosine similarity between two vectors.
|
|
58
|
-
*/
|
|
59
|
-
export function cosineSimilarity(v1: number[], v2: number[]): number {
|
|
60
|
-
if (!v1 || !v2 || v1.length !== v2.length || v1.length === 0) return 0;
|
|
61
|
-
|
|
62
|
-
let dotProduct = 0;
|
|
63
|
-
let norm1 = 0;
|
|
64
|
-
let norm2 = 0;
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < v1.length; i++) {
|
|
67
|
-
dotProduct += v1[i] * v2[i];
|
|
68
|
-
norm1 += v1[i] * v1[i];
|
|
69
|
-
norm2 += v2[i] * v2[i];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const denominator = Math.sqrt(norm1) * Math.sqrt(norm2);
|
|
73
|
-
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Perform semantic search against a list of embeddings.
|
|
78
|
-
*/
|
|
79
|
-
export function semanticSearch(queryVector: number[], entries: { embedding?: number[] }[]): number[] {
|
|
80
|
-
return entries.map(entry => {
|
|
81
|
-
if (!entry.embedding) return 0;
|
|
82
|
-
return cosineSimilarity(queryVector, entry.embedding);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pattern Index - Main Export
|
|
3
|
-
*
|
|
4
|
-
* This is the public API for the Pattern Index system.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Types
|
|
8
|
-
export type {
|
|
9
|
-
PatternType,
|
|
10
|
-
PatternEntry,
|
|
11
|
-
PatternIndex,
|
|
12
|
-
PatternIndexConfig,
|
|
13
|
-
PatternIndexStats,
|
|
14
|
-
IndexedFile,
|
|
15
|
-
PatternMatchResult,
|
|
16
|
-
PatternMatch,
|
|
17
|
-
PatternOverride,
|
|
18
|
-
StalenessResult,
|
|
19
|
-
StalenessIssue,
|
|
20
|
-
DeprecationEntry
|
|
21
|
-
} from './types.js';
|
|
22
|
-
|
|
23
|
-
// Indexer
|
|
24
|
-
export {
|
|
25
|
-
PatternIndexer,
|
|
26
|
-
savePatternIndex,
|
|
27
|
-
loadPatternIndex,
|
|
28
|
-
getDefaultIndexPath
|
|
29
|
-
} from './indexer.js';
|
|
30
|
-
|
|
31
|
-
// Matcher
|
|
32
|
-
export {
|
|
33
|
-
PatternMatcher,
|
|
34
|
-
checkPatternDuplicate,
|
|
35
|
-
type MatcherConfig
|
|
36
|
-
} from './matcher.js';
|
|
37
|
-
|
|
38
|
-
// Staleness Detection
|
|
39
|
-
export {
|
|
40
|
-
StalenessDetector,
|
|
41
|
-
checkCodeStaleness
|
|
42
|
-
} from './staleness.js';
|
|
43
|
-
|
|
44
|
-
// Security Detection
|
|
45
|
-
export {
|
|
46
|
-
SecurityDetector
|
|
47
|
-
} from './security.js';
|
|
48
|
-
|
|
49
|
-
// Override Management
|
|
50
|
-
export {
|
|
51
|
-
OverrideManager,
|
|
52
|
-
loadConfigOverrides
|
|
53
|
-
} from './overrides.js';
|
|
54
|
-
// Embeddings
|
|
55
|
-
export {
|
|
56
|
-
generateEmbedding,
|
|
57
|
-
semanticSearch,
|
|
58
|
-
cosineSimilarity
|
|
59
|
-
} from './embeddings.js';
|