@rigour-labs/core 4.3.1 → 4.3.2
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/dist/gates/frontend-secret-exposure.d.ts +27 -0
- package/dist/gates/frontend-secret-exposure.js +174 -0
- package/dist/gates/frontend-secret-exposure.test.d.ts +1 -0
- package/dist/gates/frontend-secret-exposure.test.js +95 -0
- package/dist/gates/runner.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/templates/universal-config.js +30 -0
- package/dist/types/index.d.ts +128 -0
- package/dist/types/index.js +36 -0
- package/package.json +6 -6
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Gate, GateContext } from './base.js';
|
|
2
|
+
import { Failure, Provenance } from '../types/index.js';
|
|
3
|
+
export interface FrontendSecretExposureConfig {
|
|
4
|
+
enabled?: boolean;
|
|
5
|
+
block_on_severity?: 'critical' | 'high' | 'medium' | 'low';
|
|
6
|
+
check_process_env?: boolean;
|
|
7
|
+
check_import_meta_env?: boolean;
|
|
8
|
+
secret_env_name_patterns?: string[];
|
|
9
|
+
safe_public_prefixes?: string[];
|
|
10
|
+
frontend_path_patterns?: string[];
|
|
11
|
+
server_path_patterns?: string[];
|
|
12
|
+
allowlist_env_names?: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare class FrontendSecretExposureGate extends Gate {
|
|
15
|
+
private config;
|
|
16
|
+
private severityOrder;
|
|
17
|
+
constructor(config?: FrontendSecretExposureConfig);
|
|
18
|
+
protected get provenance(): Provenance;
|
|
19
|
+
run(context: GateContext): Promise<Failure[]>;
|
|
20
|
+
private shouldSkipFile;
|
|
21
|
+
private isClientBundled;
|
|
22
|
+
private isServerOnlyContent;
|
|
23
|
+
private findEnvExposures;
|
|
24
|
+
private collectMatches;
|
|
25
|
+
private isSecretLikeEnvName;
|
|
26
|
+
private matchesAnyPattern;
|
|
27
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Gate } from './base.js';
|
|
2
|
+
import { FileScanner } from '../utils/scanner.js';
|
|
3
|
+
import { Logger } from '../utils/logger.js';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
const DEFAULT_SECRET_ENV_NAME_PATTERNS = [
|
|
7
|
+
'(?:^|_)(?:secret|private)(?:_|$)',
|
|
8
|
+
'(?:^|_)(?:token|api[_-]?key|access[_-]?key|client[_-]?secret|signing|webhook)(?:_|$)',
|
|
9
|
+
'(?:^|_)(?:db[_-]?url|database[_-]?url|connection[_-]?string)(?:_|$)',
|
|
10
|
+
];
|
|
11
|
+
const DEFAULT_SAFE_PUBLIC_PREFIXES = [
|
|
12
|
+
'NEXT_PUBLIC_',
|
|
13
|
+
'VITE_',
|
|
14
|
+
'PUBLIC_',
|
|
15
|
+
'NUXT_PUBLIC_',
|
|
16
|
+
'REACT_APP_',
|
|
17
|
+
];
|
|
18
|
+
const DEFAULT_FRONTEND_PATH_PATTERNS = [
|
|
19
|
+
'(^|/)pages/(?!api/)',
|
|
20
|
+
'(^|/)components/',
|
|
21
|
+
'(^|/)src/components/',
|
|
22
|
+
'(^|/)src/views/',
|
|
23
|
+
'(^|/)src/app/',
|
|
24
|
+
'(^|/)app/(?!api/)',
|
|
25
|
+
'(^|/)views/',
|
|
26
|
+
'(^|/)public/',
|
|
27
|
+
];
|
|
28
|
+
const DEFAULT_SERVER_PATH_PATTERNS = [
|
|
29
|
+
'(^|/)pages/api/',
|
|
30
|
+
'(^|/)src/pages/api/',
|
|
31
|
+
'(^|/)app/api/',
|
|
32
|
+
'(^|/)src/app/api/',
|
|
33
|
+
'\\.server\\.(?:ts|tsx|js|jsx|mjs|cjs)$',
|
|
34
|
+
];
|
|
35
|
+
export class FrontendSecretExposureGate extends Gate {
|
|
36
|
+
config;
|
|
37
|
+
severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
38
|
+
constructor(config = {}) {
|
|
39
|
+
super('frontend-secret-exposure', 'Frontend Secret Exposure Detection');
|
|
40
|
+
this.config = {
|
|
41
|
+
enabled: config.enabled ?? true,
|
|
42
|
+
block_on_severity: config.block_on_severity ?? 'high',
|
|
43
|
+
check_process_env: config.check_process_env ?? true,
|
|
44
|
+
check_import_meta_env: config.check_import_meta_env ?? true,
|
|
45
|
+
secret_env_name_patterns: config.secret_env_name_patterns ?? DEFAULT_SECRET_ENV_NAME_PATTERNS,
|
|
46
|
+
safe_public_prefixes: config.safe_public_prefixes ?? DEFAULT_SAFE_PUBLIC_PREFIXES,
|
|
47
|
+
frontend_path_patterns: config.frontend_path_patterns ?? DEFAULT_FRONTEND_PATH_PATTERNS,
|
|
48
|
+
server_path_patterns: config.server_path_patterns ?? DEFAULT_SERVER_PATH_PATTERNS,
|
|
49
|
+
allowlist_env_names: config.allowlist_env_names ?? [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
get provenance() { return 'security'; }
|
|
53
|
+
async run(context) {
|
|
54
|
+
if (!this.config.enabled)
|
|
55
|
+
return [];
|
|
56
|
+
const files = await FileScanner.findFiles({
|
|
57
|
+
cwd: context.cwd,
|
|
58
|
+
patterns: ['**/*.{ts,tsx,js,jsx,mjs,cjs}'],
|
|
59
|
+
ignore: [...(context.ignore || []), '**/node_modules/**', '**/dist/**', '**/build/**', '**/.next/**', '**/coverage/**'],
|
|
60
|
+
});
|
|
61
|
+
const scanFiles = files.filter(file => !this.shouldSkipFile(file));
|
|
62
|
+
const findings = [];
|
|
63
|
+
Logger.info(`Frontend Secret Exposure Gate: Scanning ${scanFiles.length} files`);
|
|
64
|
+
for (const file of scanFiles) {
|
|
65
|
+
const fullPath = path.join(context.cwd, file);
|
|
66
|
+
let content = '';
|
|
67
|
+
try {
|
|
68
|
+
content = await fs.readFile(fullPath, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!this.isClientBundled(file, content))
|
|
74
|
+
continue;
|
|
75
|
+
findings.push(...this.findEnvExposures(file, content));
|
|
76
|
+
}
|
|
77
|
+
findings.sort((a, b) => this.severityOrder[a.severity] - this.severityOrder[b.severity]);
|
|
78
|
+
const threshold = this.severityOrder[this.config.block_on_severity];
|
|
79
|
+
return findings
|
|
80
|
+
.filter(f => this.severityOrder[f.severity] <= threshold)
|
|
81
|
+
.map(f => this.createFailure(`Potential frontend secret exposure: ${f.source}.${f.envVar} is referenced in client-bundled code.`, [f.file], 'Move secret usage to server-only code (API route/server action) and expose only public-safe values.', 'Security: Frontend Secret Exposure', f.line, f.line, f.severity));
|
|
82
|
+
}
|
|
83
|
+
shouldSkipFile(file) {
|
|
84
|
+
const normalized = file.replace(/\\/g, '/');
|
|
85
|
+
if (/\.(test|spec)\.(?:ts|tsx|js|jsx|mjs|cjs)$/i.test(normalized))
|
|
86
|
+
return true;
|
|
87
|
+
if (/\/(?:__tests__|tests|test|__test__|e2e|fixtures|mocks)\//.test(`/${normalized}`))
|
|
88
|
+
return true;
|
|
89
|
+
if (/\/(?:examples|studio-dist)\//.test(`/${normalized}`))
|
|
90
|
+
return true;
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
isClientBundled(file, content) {
|
|
94
|
+
const normalized = file.replace(/\\/g, '/');
|
|
95
|
+
if (this.matchesAnyPattern(normalized, this.config.server_path_patterns))
|
|
96
|
+
return false;
|
|
97
|
+
if (this.isServerOnlyContent(content))
|
|
98
|
+
return false;
|
|
99
|
+
if (/^\s*['"]use client['"]\s*;?/m.test(content))
|
|
100
|
+
return true;
|
|
101
|
+
if (this.matchesAnyPattern(normalized, this.config.frontend_path_patterns))
|
|
102
|
+
return true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
isServerOnlyContent(content) {
|
|
106
|
+
if (/from\s+['"]server-only['"]/.test(content))
|
|
107
|
+
return true;
|
|
108
|
+
if (/import\s+['"]server-only['"]/.test(content))
|
|
109
|
+
return true;
|
|
110
|
+
if (/export\s+async\s+function\s+getServerSideProps\s*\(/.test(content))
|
|
111
|
+
return true;
|
|
112
|
+
if (/export\s+async\s+function\s+getStaticProps\s*\(/.test(content))
|
|
113
|
+
return true;
|
|
114
|
+
if (/['"]use server['"]/.test(content))
|
|
115
|
+
return true;
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
findEnvExposures(file, content) {
|
|
119
|
+
const findings = [];
|
|
120
|
+
if (this.config.check_process_env) {
|
|
121
|
+
const processEnvRegex = /process\.env\.([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
122
|
+
findings.push(...this.collectMatches(file, content, processEnvRegex, 'process.env'));
|
|
123
|
+
}
|
|
124
|
+
if (this.config.check_import_meta_env) {
|
|
125
|
+
const importMetaRegex = /import\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
126
|
+
findings.push(...this.collectMatches(file, content, importMetaRegex, 'import.meta.env'));
|
|
127
|
+
}
|
|
128
|
+
return findings;
|
|
129
|
+
}
|
|
130
|
+
collectMatches(file, content, regex, source) {
|
|
131
|
+
const matches = [];
|
|
132
|
+
const scanRegex = new RegExp(regex.source, 'g');
|
|
133
|
+
for (const match of content.matchAll(scanRegex)) {
|
|
134
|
+
const envVar = match[1];
|
|
135
|
+
if (!this.isSecretLikeEnvName(envVar))
|
|
136
|
+
continue;
|
|
137
|
+
const startIndex = match.index ?? 0;
|
|
138
|
+
const beforeMatch = content.slice(0, startIndex);
|
|
139
|
+
const line = beforeMatch.split('\n').length;
|
|
140
|
+
matches.push({
|
|
141
|
+
file,
|
|
142
|
+
line,
|
|
143
|
+
envVar,
|
|
144
|
+
source,
|
|
145
|
+
severity: 'high',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return matches;
|
|
149
|
+
}
|
|
150
|
+
isSecretLikeEnvName(envVar) {
|
|
151
|
+
if (this.config.allowlist_env_names.includes(envVar))
|
|
152
|
+
return false;
|
|
153
|
+
if (this.config.safe_public_prefixes.some(prefix => envVar.startsWith(prefix)))
|
|
154
|
+
return false;
|
|
155
|
+
return this.config.secret_env_name_patterns.some(pattern => {
|
|
156
|
+
try {
|
|
157
|
+
return new RegExp(pattern, 'i').test(envVar);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
matchesAnyPattern(value, patterns) {
|
|
165
|
+
return patterns.some(pattern => {
|
|
166
|
+
try {
|
|
167
|
+
return new RegExp(pattern, 'i').test(value);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { FrontendSecretExposureGate } from './frontend-secret-exposure.js';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
describe('FrontendSecretExposureGate', () => {
|
|
7
|
+
let testDir;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'frontend-secret-test-'));
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
fs.rmSync(testDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
it('detects process.env secret usage in client-bundled file', async () => {
|
|
15
|
+
const filePath = path.join(testDir, 'src/components/Checkout.tsx');
|
|
16
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17
|
+
fs.writeFileSync(filePath, `
|
|
18
|
+
export function Checkout() {
|
|
19
|
+
const key = process.env.STRIPE_SECRET_KEY;
|
|
20
|
+
return <div>{key}</div>;
|
|
21
|
+
}
|
|
22
|
+
`);
|
|
23
|
+
const gate = new FrontendSecretExposureGate();
|
|
24
|
+
const failures = await gate.run({ cwd: testDir });
|
|
25
|
+
expect(failures.length).toBeGreaterThan(0);
|
|
26
|
+
expect(failures[0].id).toBe('frontend-secret-exposure');
|
|
27
|
+
expect(failures[0].files).toContain('src/components/Checkout.tsx');
|
|
28
|
+
});
|
|
29
|
+
it('detects import.meta.env secret usage in frontend app path', async () => {
|
|
30
|
+
const filePath = path.join(testDir, 'src/app/page.tsx');
|
|
31
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
32
|
+
fs.writeFileSync(filePath, `
|
|
33
|
+
export default function Page() {
|
|
34
|
+
return <span>{import.meta.env.OPENAI_API_KEY}</span>;
|
|
35
|
+
}
|
|
36
|
+
`);
|
|
37
|
+
const gate = new FrontendSecretExposureGate();
|
|
38
|
+
const failures = await gate.run({ cwd: testDir });
|
|
39
|
+
expect(failures.length).toBeGreaterThan(0);
|
|
40
|
+
});
|
|
41
|
+
it('does not flag public env prefixes in client files', async () => {
|
|
42
|
+
const filePath = path.join(testDir, 'components/Header.tsx');
|
|
43
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
44
|
+
fs.writeFileSync(filePath, `
|
|
45
|
+
export const key = process.env.NEXT_PUBLIC_STRIPE_KEY;
|
|
46
|
+
`);
|
|
47
|
+
const gate = new FrontendSecretExposureGate();
|
|
48
|
+
const failures = await gate.run({ cwd: testDir });
|
|
49
|
+
expect(failures).toHaveLength(0);
|
|
50
|
+
});
|
|
51
|
+
it('does not flag server-only API route', async () => {
|
|
52
|
+
const filePath = path.join(testDir, 'pages/api/charge.ts');
|
|
53
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
54
|
+
fs.writeFileSync(filePath, `
|
|
55
|
+
export default function handler() {
|
|
56
|
+
return process.env.STRIPE_SECRET_KEY;
|
|
57
|
+
}
|
|
58
|
+
`);
|
|
59
|
+
const gate = new FrontendSecretExposureGate();
|
|
60
|
+
const failures = await gate.run({ cwd: testDir });
|
|
61
|
+
expect(failures).toHaveLength(0);
|
|
62
|
+
});
|
|
63
|
+
it('does not flag .server files', async () => {
|
|
64
|
+
const filePath = path.join(testDir, 'src/lib/payments.server.ts');
|
|
65
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
66
|
+
fs.writeFileSync(filePath, `
|
|
67
|
+
export const stripeSecret = process.env.STRIPE_SECRET_KEY;
|
|
68
|
+
`);
|
|
69
|
+
const gate = new FrontendSecretExposureGate();
|
|
70
|
+
const failures = await gate.run({ cwd: testDir });
|
|
71
|
+
expect(failures).toHaveLength(0);
|
|
72
|
+
});
|
|
73
|
+
it('respects explicit allowlist env names', async () => {
|
|
74
|
+
const filePath = path.join(testDir, 'src/views/App.tsx');
|
|
75
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
76
|
+
fs.writeFileSync(filePath, `
|
|
77
|
+
export const x = process.env.INTERNAL_TOKEN_FOR_DOCS;
|
|
78
|
+
`);
|
|
79
|
+
const gate = new FrontendSecretExposureGate({
|
|
80
|
+
allowlist_env_names: ['INTERNAL_TOKEN_FOR_DOCS'],
|
|
81
|
+
});
|
|
82
|
+
const failures = await gate.run({ cwd: testDir });
|
|
83
|
+
expect(failures).toHaveLength(0);
|
|
84
|
+
});
|
|
85
|
+
it('skips when disabled', async () => {
|
|
86
|
+
const filePath = path.join(testDir, 'src/components/Client.tsx');
|
|
87
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
88
|
+
fs.writeFileSync(filePath, `
|
|
89
|
+
export const x = process.env.OPENAI_API_KEY;
|
|
90
|
+
`);
|
|
91
|
+
const gate = new FrontendSecretExposureGate({ enabled: false });
|
|
92
|
+
const failures = await gate.run({ cwd: testDir });
|
|
93
|
+
expect(failures).toHaveLength(0);
|
|
94
|
+
});
|
|
95
|
+
});
|
package/dist/gates/runner.js
CHANGED
|
@@ -15,6 +15,7 @@ import { RetryLoopBreakerGate } from './retry-loop-breaker.js';
|
|
|
15
15
|
import { AgentTeamGate } from './agent-team.js';
|
|
16
16
|
import { CheckpointGate } from './checkpoint.js';
|
|
17
17
|
import { SecurityPatternsGate } from './security-patterns.js';
|
|
18
|
+
import { FrontendSecretExposureGate } from './frontend-secret-exposure.js';
|
|
18
19
|
import { DuplicationDriftGate } from './duplication-drift.js';
|
|
19
20
|
import { HallucinatedImportsGate } from './hallucinated-imports.js';
|
|
20
21
|
import { InconsistentErrorHandlingGate } from './inconsistent-error-handling.js';
|
|
@@ -67,6 +68,9 @@ export class GateRunner {
|
|
|
67
68
|
if (this.config.gates.security?.enabled !== false) {
|
|
68
69
|
this.gates.push(new SecurityPatternsGate(this.config.gates.security));
|
|
69
70
|
}
|
|
71
|
+
if (this.config.gates.frontend_secret_exposure?.enabled !== false) {
|
|
72
|
+
this.gates.push(new FrontendSecretExposureGate(this.config.gates.frontend_secret_exposure));
|
|
73
|
+
}
|
|
70
74
|
// v2.16+ AI-Native Drift Detection Gates (enabled by default)
|
|
71
75
|
if (this.config.gates.duplication_drift?.enabled !== false) {
|
|
72
76
|
this.gates.push(new DuplicationDriftGate(this.config.gates.duplication_drift));
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './types/fix-packet.js';
|
|
|
7
7
|
export { Gate, GateContext } from './gates/base.js';
|
|
8
8
|
export { RetryLoopBreakerGate } from './gates/retry-loop-breaker.js';
|
|
9
9
|
export { SideEffectAnalysisGate } from './gates/side-effect-analysis.js';
|
|
10
|
+
export { FrontendSecretExposureGate } from './gates/frontend-secret-exposure.js';
|
|
10
11
|
export * from './utils/logger.js';
|
|
11
12
|
export * from './services/score-history.js';
|
|
12
13
|
export * from './hooks/index.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './types/fix-packet.js';
|
|
|
7
7
|
export { Gate } from './gates/base.js';
|
|
8
8
|
export { RetryLoopBreakerGate } from './gates/retry-loop-breaker.js';
|
|
9
9
|
export { SideEffectAnalysisGate } from './gates/side-effect-analysis.js';
|
|
10
|
+
export { FrontendSecretExposureGate } from './gates/frontend-secret-exposure.js';
|
|
10
11
|
export * from './utils/logger.js';
|
|
11
12
|
export * from './services/score-history.js';
|
|
12
13
|
export * from './hooks/index.js';
|
|
@@ -72,6 +72,36 @@ export const UNIVERSAL_CONFIG = {
|
|
|
72
72
|
command_injection: true,
|
|
73
73
|
block_on_severity: 'high',
|
|
74
74
|
},
|
|
75
|
+
frontend_secret_exposure: {
|
|
76
|
+
enabled: true,
|
|
77
|
+
block_on_severity: 'high',
|
|
78
|
+
check_process_env: true,
|
|
79
|
+
check_import_meta_env: true,
|
|
80
|
+
secret_env_name_patterns: [
|
|
81
|
+
'(?:^|_)(?:secret|private)(?:_|$)',
|
|
82
|
+
'(?:^|_)(?:token|api[_-]?key|access[_-]?key|client[_-]?secret|signing|webhook)(?:_|$)',
|
|
83
|
+
'(?:^|_)(?:db[_-]?url|database[_-]?url|connection[_-]?string)(?:_|$)',
|
|
84
|
+
],
|
|
85
|
+
safe_public_prefixes: ['NEXT_PUBLIC_', 'VITE_', 'PUBLIC_', 'NUXT_PUBLIC_', 'REACT_APP_'],
|
|
86
|
+
frontend_path_patterns: [
|
|
87
|
+
'(^|/)pages/(?!api/)',
|
|
88
|
+
'(^|/)components/',
|
|
89
|
+
'(^|/)src/components/',
|
|
90
|
+
'(^|/)src/views/',
|
|
91
|
+
'(^|/)src/app/',
|
|
92
|
+
'(^|/)app/(?!api/)',
|
|
93
|
+
'(^|/)views/',
|
|
94
|
+
'(^|/)public/',
|
|
95
|
+
],
|
|
96
|
+
server_path_patterns: [
|
|
97
|
+
'(^|/)pages/api/',
|
|
98
|
+
'(^|/)src/pages/api/',
|
|
99
|
+
'(^|/)app/api/',
|
|
100
|
+
'(^|/)src/app/api/',
|
|
101
|
+
'\\.server\\.(?:ts|tsx|js|jsx|mjs|cjs)$',
|
|
102
|
+
],
|
|
103
|
+
allowlist_env_names: [],
|
|
104
|
+
},
|
|
75
105
|
adaptive: {
|
|
76
106
|
enabled: false,
|
|
77
107
|
base_coverage_threshold: 80,
|
package/dist/types/index.d.ts
CHANGED
|
@@ -213,6 +213,37 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
213
213
|
command_injection?: boolean | undefined;
|
|
214
214
|
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
215
215
|
}>>>;
|
|
216
|
+
frontend_secret_exposure: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
217
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
218
|
+
block_on_severity: z.ZodDefault<z.ZodOptional<z.ZodEnum<["critical", "high", "medium", "low"]>>>;
|
|
219
|
+
check_process_env: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
220
|
+
check_import_meta_env: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
221
|
+
secret_env_name_patterns: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
222
|
+
safe_public_prefixes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
223
|
+
frontend_path_patterns: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
224
|
+
server_path_patterns: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
225
|
+
allowlist_env_names: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
226
|
+
}, "strip", z.ZodTypeAny, {
|
|
227
|
+
enabled: boolean;
|
|
228
|
+
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
229
|
+
check_process_env: boolean;
|
|
230
|
+
check_import_meta_env: boolean;
|
|
231
|
+
secret_env_name_patterns: string[];
|
|
232
|
+
safe_public_prefixes: string[];
|
|
233
|
+
frontend_path_patterns: string[];
|
|
234
|
+
server_path_patterns: string[];
|
|
235
|
+
allowlist_env_names: string[];
|
|
236
|
+
}, {
|
|
237
|
+
enabled?: boolean | undefined;
|
|
238
|
+
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
239
|
+
check_process_env?: boolean | undefined;
|
|
240
|
+
check_import_meta_env?: boolean | undefined;
|
|
241
|
+
secret_env_name_patterns?: string[] | undefined;
|
|
242
|
+
safe_public_prefixes?: string[] | undefined;
|
|
243
|
+
frontend_path_patterns?: string[] | undefined;
|
|
244
|
+
server_path_patterns?: string[] | undefined;
|
|
245
|
+
allowlist_env_names?: string[] | undefined;
|
|
246
|
+
}>>>;
|
|
216
247
|
adaptive: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
217
248
|
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
218
249
|
base_coverage_threshold: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -674,6 +705,17 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
674
705
|
command_injection: boolean;
|
|
675
706
|
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
676
707
|
};
|
|
708
|
+
frontend_secret_exposure: {
|
|
709
|
+
enabled: boolean;
|
|
710
|
+
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
711
|
+
check_process_env: boolean;
|
|
712
|
+
check_import_meta_env: boolean;
|
|
713
|
+
secret_env_name_patterns: string[];
|
|
714
|
+
safe_public_prefixes: string[];
|
|
715
|
+
frontend_path_patterns: string[];
|
|
716
|
+
server_path_patterns: string[];
|
|
717
|
+
allowlist_env_names: string[];
|
|
718
|
+
};
|
|
677
719
|
adaptive: {
|
|
678
720
|
enabled: boolean;
|
|
679
721
|
base_coverage_threshold: number;
|
|
@@ -874,6 +916,17 @@ export declare const GatesSchema: z.ZodObject<{
|
|
|
874
916
|
command_injection?: boolean | undefined;
|
|
875
917
|
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
876
918
|
} | undefined;
|
|
919
|
+
frontend_secret_exposure?: {
|
|
920
|
+
enabled?: boolean | undefined;
|
|
921
|
+
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
922
|
+
check_process_env?: boolean | undefined;
|
|
923
|
+
check_import_meta_env?: boolean | undefined;
|
|
924
|
+
secret_env_name_patterns?: string[] | undefined;
|
|
925
|
+
safe_public_prefixes?: string[] | undefined;
|
|
926
|
+
frontend_path_patterns?: string[] | undefined;
|
|
927
|
+
server_path_patterns?: string[] | undefined;
|
|
928
|
+
allowlist_env_names?: string[] | undefined;
|
|
929
|
+
} | undefined;
|
|
877
930
|
adaptive?: {
|
|
878
931
|
enabled?: boolean | undefined;
|
|
879
932
|
base_coverage_threshold?: number | undefined;
|
|
@@ -1245,6 +1298,37 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
1245
1298
|
command_injection?: boolean | undefined;
|
|
1246
1299
|
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
1247
1300
|
}>>>;
|
|
1301
|
+
frontend_secret_exposure: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1302
|
+
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1303
|
+
block_on_severity: z.ZodDefault<z.ZodOptional<z.ZodEnum<["critical", "high", "medium", "low"]>>>;
|
|
1304
|
+
check_process_env: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1305
|
+
check_import_meta_env: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1306
|
+
secret_env_name_patterns: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1307
|
+
safe_public_prefixes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1308
|
+
frontend_path_patterns: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1309
|
+
server_path_patterns: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1310
|
+
allowlist_env_names: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
1311
|
+
}, "strip", z.ZodTypeAny, {
|
|
1312
|
+
enabled: boolean;
|
|
1313
|
+
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
1314
|
+
check_process_env: boolean;
|
|
1315
|
+
check_import_meta_env: boolean;
|
|
1316
|
+
secret_env_name_patterns: string[];
|
|
1317
|
+
safe_public_prefixes: string[];
|
|
1318
|
+
frontend_path_patterns: string[];
|
|
1319
|
+
server_path_patterns: string[];
|
|
1320
|
+
allowlist_env_names: string[];
|
|
1321
|
+
}, {
|
|
1322
|
+
enabled?: boolean | undefined;
|
|
1323
|
+
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
1324
|
+
check_process_env?: boolean | undefined;
|
|
1325
|
+
check_import_meta_env?: boolean | undefined;
|
|
1326
|
+
secret_env_name_patterns?: string[] | undefined;
|
|
1327
|
+
safe_public_prefixes?: string[] | undefined;
|
|
1328
|
+
frontend_path_patterns?: string[] | undefined;
|
|
1329
|
+
server_path_patterns?: string[] | undefined;
|
|
1330
|
+
allowlist_env_names?: string[] | undefined;
|
|
1331
|
+
}>>>;
|
|
1248
1332
|
adaptive: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1249
1333
|
enabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
1250
1334
|
base_coverage_threshold: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -1706,6 +1790,17 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
1706
1790
|
command_injection: boolean;
|
|
1707
1791
|
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
1708
1792
|
};
|
|
1793
|
+
frontend_secret_exposure: {
|
|
1794
|
+
enabled: boolean;
|
|
1795
|
+
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
1796
|
+
check_process_env: boolean;
|
|
1797
|
+
check_import_meta_env: boolean;
|
|
1798
|
+
secret_env_name_patterns: string[];
|
|
1799
|
+
safe_public_prefixes: string[];
|
|
1800
|
+
frontend_path_patterns: string[];
|
|
1801
|
+
server_path_patterns: string[];
|
|
1802
|
+
allowlist_env_names: string[];
|
|
1803
|
+
};
|
|
1709
1804
|
adaptive: {
|
|
1710
1805
|
enabled: boolean;
|
|
1711
1806
|
base_coverage_threshold: number;
|
|
@@ -1906,6 +2001,17 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
1906
2001
|
command_injection?: boolean | undefined;
|
|
1907
2002
|
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
1908
2003
|
} | undefined;
|
|
2004
|
+
frontend_secret_exposure?: {
|
|
2005
|
+
enabled?: boolean | undefined;
|
|
2006
|
+
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
2007
|
+
check_process_env?: boolean | undefined;
|
|
2008
|
+
check_import_meta_env?: boolean | undefined;
|
|
2009
|
+
secret_env_name_patterns?: string[] | undefined;
|
|
2010
|
+
safe_public_prefixes?: string[] | undefined;
|
|
2011
|
+
frontend_path_patterns?: string[] | undefined;
|
|
2012
|
+
server_path_patterns?: string[] | undefined;
|
|
2013
|
+
allowlist_env_names?: string[] | undefined;
|
|
2014
|
+
} | undefined;
|
|
1909
2015
|
adaptive?: {
|
|
1910
2016
|
enabled?: boolean | undefined;
|
|
1911
2017
|
base_coverage_threshold?: number | undefined;
|
|
@@ -2148,6 +2254,17 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2148
2254
|
command_injection: boolean;
|
|
2149
2255
|
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
2150
2256
|
};
|
|
2257
|
+
frontend_secret_exposure: {
|
|
2258
|
+
enabled: boolean;
|
|
2259
|
+
block_on_severity: "critical" | "high" | "medium" | "low";
|
|
2260
|
+
check_process_env: boolean;
|
|
2261
|
+
check_import_meta_env: boolean;
|
|
2262
|
+
secret_env_name_patterns: string[];
|
|
2263
|
+
safe_public_prefixes: string[];
|
|
2264
|
+
frontend_path_patterns: string[];
|
|
2265
|
+
server_path_patterns: string[];
|
|
2266
|
+
allowlist_env_names: string[];
|
|
2267
|
+
};
|
|
2151
2268
|
adaptive: {
|
|
2152
2269
|
enabled: boolean;
|
|
2153
2270
|
base_coverage_threshold: number;
|
|
@@ -2374,6 +2491,17 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2374
2491
|
command_injection?: boolean | undefined;
|
|
2375
2492
|
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
2376
2493
|
} | undefined;
|
|
2494
|
+
frontend_secret_exposure?: {
|
|
2495
|
+
enabled?: boolean | undefined;
|
|
2496
|
+
block_on_severity?: "critical" | "high" | "medium" | "low" | undefined;
|
|
2497
|
+
check_process_env?: boolean | undefined;
|
|
2498
|
+
check_import_meta_env?: boolean | undefined;
|
|
2499
|
+
secret_env_name_patterns?: string[] | undefined;
|
|
2500
|
+
safe_public_prefixes?: string[] | undefined;
|
|
2501
|
+
frontend_path_patterns?: string[] | undefined;
|
|
2502
|
+
server_path_patterns?: string[] | undefined;
|
|
2503
|
+
allowlist_env_names?: string[] | undefined;
|
|
2504
|
+
} | undefined;
|
|
2377
2505
|
adaptive?: {
|
|
2378
2506
|
enabled?: boolean | undefined;
|
|
2379
2507
|
base_coverage_threshold?: number | undefined;
|
package/dist/types/index.js
CHANGED
|
@@ -95,6 +95,42 @@ export const GatesSchema = z.object({
|
|
|
95
95
|
command_injection: z.boolean().optional().default(true),
|
|
96
96
|
block_on_severity: z.enum(['critical', 'high', 'medium', 'low']).optional().default('high'),
|
|
97
97
|
}).optional().default({}),
|
|
98
|
+
frontend_secret_exposure: z.object({
|
|
99
|
+
enabled: z.boolean().optional().default(true),
|
|
100
|
+
block_on_severity: z.enum(['critical', 'high', 'medium', 'low']).optional().default('high'),
|
|
101
|
+
check_process_env: z.boolean().optional().default(true),
|
|
102
|
+
check_import_meta_env: z.boolean().optional().default(true),
|
|
103
|
+
secret_env_name_patterns: z.array(z.string()).optional().default([
|
|
104
|
+
'(?:^|_)(?:secret|private)(?:_|$)',
|
|
105
|
+
'(?:^|_)(?:token|api[_-]?key|access[_-]?key|client[_-]?secret|signing|webhook)(?:_|$)',
|
|
106
|
+
'(?:^|_)(?:db[_-]?url|database[_-]?url|connection[_-]?string)(?:_|$)',
|
|
107
|
+
]),
|
|
108
|
+
safe_public_prefixes: z.array(z.string()).optional().default([
|
|
109
|
+
'NEXT_PUBLIC_',
|
|
110
|
+
'VITE_',
|
|
111
|
+
'PUBLIC_',
|
|
112
|
+
'NUXT_PUBLIC_',
|
|
113
|
+
'REACT_APP_',
|
|
114
|
+
]),
|
|
115
|
+
frontend_path_patterns: z.array(z.string()).optional().default([
|
|
116
|
+
'(^|/)pages/(?!api/)',
|
|
117
|
+
'(^|/)components/',
|
|
118
|
+
'(^|/)src/components/',
|
|
119
|
+
'(^|/)src/views/',
|
|
120
|
+
'(^|/)src/app/',
|
|
121
|
+
'(^|/)app/(?!api/)',
|
|
122
|
+
'(^|/)views/',
|
|
123
|
+
'(^|/)public/',
|
|
124
|
+
]),
|
|
125
|
+
server_path_patterns: z.array(z.string()).optional().default([
|
|
126
|
+
'(^|/)pages/api/',
|
|
127
|
+
'(^|/)src/pages/api/',
|
|
128
|
+
'(^|/)app/api/',
|
|
129
|
+
'(^|/)src/app/api/',
|
|
130
|
+
'\\.server\\.(?:ts|tsx|js|jsx|mjs|cjs)$',
|
|
131
|
+
]),
|
|
132
|
+
allowlist_env_names: z.array(z.string()).optional().default([]),
|
|
133
|
+
}).optional().default({}),
|
|
98
134
|
adaptive: z.object({
|
|
99
135
|
enabled: z.boolean().optional().default(false),
|
|
100
136
|
base_coverage_threshold: z.number().optional().default(80),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/core",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "Deterministic quality gate engine for AI-generated code. AST analysis, drift detection, and Fix Packet generation across TypeScript, JavaScript, Python, Go, Ruby, and C#.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://rigour.run",
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
"@xenova/transformers": "^2.17.2",
|
|
60
60
|
"better-sqlite3": "^11.0.0",
|
|
61
61
|
"openai": "^4.104.0",
|
|
62
|
-
"@rigour-labs/brain-darwin-arm64": "4.3.
|
|
63
|
-
"@rigour-labs/brain-
|
|
64
|
-
"@rigour-labs/brain-
|
|
65
|
-
"@rigour-labs/brain-win-x64": "4.3.
|
|
66
|
-
"@rigour-labs/brain-linux-x64": "4.3.
|
|
62
|
+
"@rigour-labs/brain-darwin-arm64": "4.3.2",
|
|
63
|
+
"@rigour-labs/brain-linux-arm64": "4.3.2",
|
|
64
|
+
"@rigour-labs/brain-darwin-x64": "4.3.2",
|
|
65
|
+
"@rigour-labs/brain-win-x64": "4.3.2",
|
|
66
|
+
"@rigour-labs/brain-linux-x64": "4.3.2"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/better-sqlite3": "^7.6.12",
|