semantic-complexity 0.0.7-741984fb → 0.0.15-29c6b296
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/__tests__/analyzers.test.d.ts +5 -0
- package/dist/__tests__/analyzers.test.d.ts.map +1 -0
- package/dist/__tests__/analyzers.test.js +357 -0
- package/dist/__tests__/analyzers.test.js.map +1 -0
- package/dist/__tests__/gate.test.d.ts +5 -0
- package/dist/__tests__/gate.test.d.ts.map +1 -0
- package/dist/__tests__/gate.test.js +140 -0
- package/dist/__tests__/gate.test.js.map +1 -0
- package/dist/analyzers/bread.d.ts +63 -0
- package/dist/analyzers/bread.d.ts.map +1 -0
- package/dist/analyzers/bread.js +415 -0
- package/dist/analyzers/bread.js.map +1 -0
- package/dist/analyzers/cheese.d.ts +111 -0
- package/dist/analyzers/cheese.d.ts.map +1 -0
- package/dist/analyzers/cheese.js +881 -0
- package/dist/analyzers/cheese.js.map +1 -0
- package/dist/analyzers/ham.d.ts +33 -0
- package/dist/analyzers/ham.d.ts.map +1 -0
- package/dist/analyzers/ham.js +264 -0
- package/dist/analyzers/ham.js.map +1 -0
- package/dist/analyzers/index.d.ts +7 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +7 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/budget/index.d.ts +50 -0
- package/dist/budget/index.d.ts.map +1 -0
- package/dist/budget/index.js +138 -0
- package/dist/budget/index.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +103 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/gate/index.d.ts +31 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/index.js +117 -0
- package/dist/gate/index.js.map +1 -0
- package/dist/gate/waiver.d.ts +83 -0
- package/dist/gate/waiver.d.ts.map +1 -0
- package/dist/gate/waiver.js +425 -0
- package/dist/gate/waiver.js.map +1 -0
- package/dist/index.d.ts +8 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -49
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +351 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/recommend/index.d.ts +32 -0
- package/dist/recommend/index.d.ts.map +1 -0
- package/dist/recommend/index.js +169 -0
- package/dist/recommend/index.js.map +1 -0
- package/dist/simplex/index.d.ts +26 -0
- package/dist/simplex/index.d.ts.map +1 -0
- package/dist/simplex/index.js +100 -0
- package/dist/simplex/index.js.map +1 -0
- package/dist/types/index.d.ts +39 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +27 -52
- package/LICENSE +0 -21
- package/README.md +0 -72
- package/dist/ast/index.d.ts +0 -2
- package/dist/ast/index.d.ts.map +0 -1
- package/dist/ast/index.js +0 -2
- package/dist/ast/index.js.map +0 -1
- package/dist/ast/parser.d.ts +0 -27
- package/dist/ast/parser.d.ts.map +0 -1
- package/dist/ast/parser.js +0 -273
- package/dist/ast/parser.js.map +0 -1
- package/dist/canonical/convergence.d.ts +0 -56
- package/dist/canonical/convergence.d.ts.map +0 -1
- package/dist/canonical/convergence.js +0 -149
- package/dist/canonical/convergence.js.map +0 -1
- package/dist/canonical/index.d.ts +0 -11
- package/dist/canonical/index.d.ts.map +0 -1
- package/dist/canonical/index.js +0 -11
- package/dist/canonical/index.js.map +0 -1
- package/dist/canonical/profiles.d.ts +0 -40
- package/dist/canonical/profiles.d.ts.map +0 -1
- package/dist/canonical/profiles.js +0 -182
- package/dist/canonical/profiles.js.map +0 -1
- package/dist/canonical/types.d.ts +0 -124
- package/dist/canonical/types.d.ts.map +0 -1
- package/dist/canonical/types.js +0 -46
- package/dist/canonical/types.js.map +0 -1
- package/dist/compare.d.ts +0 -31
- package/dist/compare.d.ts.map +0 -1
- package/dist/compare.js +0 -354
- package/dist/compare.js.map +0 -1
- package/dist/context/index.d.ts +0 -41
- package/dist/context/index.d.ts.map +0 -1
- package/dist/context/index.js +0 -253
- package/dist/context/index.js.map +0 -1
- package/dist/gates/delta.d.ts +0 -45
- package/dist/gates/delta.d.ts.map +0 -1
- package/dist/gates/delta.js +0 -260
- package/dist/gates/delta.js.map +0 -1
- package/dist/gates/index.d.ts +0 -9
- package/dist/gates/index.d.ts.map +0 -1
- package/dist/gates/index.js +0 -9
- package/dist/gates/index.js.map +0 -1
- package/dist/gates/types.d.ts +0 -96
- package/dist/gates/types.d.ts.map +0 -1
- package/dist/gates/types.js +0 -59
- package/dist/gates/types.js.map +0 -1
- package/dist/graph/call.d.ts +0 -63
- package/dist/graph/call.d.ts.map +0 -1
- package/dist/graph/call.js +0 -240
- package/dist/graph/call.js.map +0 -1
- package/dist/graph/dependency.d.ts +0 -52
- package/dist/graph/dependency.d.ts.map +0 -1
- package/dist/graph/dependency.js +0 -296
- package/dist/graph/dependency.js.map +0 -1
- package/dist/graph/index.d.ts +0 -3
- package/dist/graph/index.d.ts.map +0 -1
- package/dist/graph/index.js +0 -3
- package/dist/graph/index.js.map +0 -1
- package/dist/metrics/cognitive.d.ts +0 -42
- package/dist/metrics/cognitive.d.ts.map +0 -1
- package/dist/metrics/cognitive.js +0 -204
- package/dist/metrics/cognitive.js.map +0 -1
- package/dist/metrics/cyclomatic.d.ts +0 -31
- package/dist/metrics/cyclomatic.d.ts.map +0 -1
- package/dist/metrics/cyclomatic.js +0 -121
- package/dist/metrics/cyclomatic.js.map +0 -1
- package/dist/metrics/dimensional.d.ts +0 -32
- package/dist/metrics/dimensional.d.ts.map +0 -1
- package/dist/metrics/dimensional.js +0 -560
- package/dist/metrics/dimensional.js.map +0 -1
- package/dist/metrics/index.d.ts +0 -26
- package/dist/metrics/index.d.ts.map +0 -1
- package/dist/metrics/index.js +0 -120
- package/dist/metrics/index.js.map +0 -1
- package/dist/metrics/meta.d.ts +0 -90
- package/dist/metrics/meta.d.ts.map +0 -1
- package/dist/metrics/meta.js +0 -144
- package/dist/metrics/meta.js.map +0 -1
- package/dist/tensor/canonical.d.ts +0 -53
- package/dist/tensor/canonical.d.ts.map +0 -1
- package/dist/tensor/canonical.js +0 -192
- package/dist/tensor/canonical.js.map +0 -1
- package/dist/tensor/index.d.ts +0 -22
- package/dist/tensor/index.d.ts.map +0 -1
- package/dist/tensor/index.js +0 -22
- package/dist/tensor/index.js.map +0 -1
- package/dist/tensor/matrix.d.ts +0 -62
- package/dist/tensor/matrix.d.ts.map +0 -1
- package/dist/tensor/matrix.js +0 -187
- package/dist/tensor/matrix.js.map +0 -1
- package/dist/tensor/scoring.d.ts +0 -82
- package/dist/tensor/scoring.d.ts.map +0 -1
- package/dist/tensor/scoring.js +0 -233
- package/dist/tensor/scoring.js.map +0 -1
- package/dist/tensor/types.d.ts +0 -186
- package/dist/tensor/types.d.ts.map +0 -1
- package/dist/tensor/types.js +0 -15
- package/dist/tensor/types.js.map +0 -1
- package/dist/types.d.ts +0 -341
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -22
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzers.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/analyzers.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { analyzeBread } from '../analyzers/bread.js';
|
|
7
|
+
import { analyzeCheese } from '../analyzers/cheese.js';
|
|
8
|
+
import { analyzeHam } from '../analyzers/ham.js';
|
|
9
|
+
describe('Bread Analyzer', () => {
|
|
10
|
+
it('should detect trust boundary markers', () => {
|
|
11
|
+
const source = `
|
|
12
|
+
// TRUST_BOUNDARY: API endpoint
|
|
13
|
+
app.get('/api/users', handler);
|
|
14
|
+
`;
|
|
15
|
+
const result = analyzeBread(source);
|
|
16
|
+
assert.ok(result.trustBoundaryCount > 0);
|
|
17
|
+
});
|
|
18
|
+
it('should detect hardcoded secrets', () => {
|
|
19
|
+
const source = `
|
|
20
|
+
const password = "secret123";
|
|
21
|
+
const api_key = "sk_live_xxx";
|
|
22
|
+
`;
|
|
23
|
+
const result = analyzeBread(source);
|
|
24
|
+
assert.ok(result.secretPatterns.length > 0);
|
|
25
|
+
assert.ok(result.secretPatterns.some(s => s.severity === 'high'));
|
|
26
|
+
});
|
|
27
|
+
it('should count hidden dependencies', () => {
|
|
28
|
+
const source = `
|
|
29
|
+
const env = process.env.NODE_ENV;
|
|
30
|
+
console.log(env);
|
|
31
|
+
`;
|
|
32
|
+
const result = analyzeBread(source);
|
|
33
|
+
assert.ok(result.hiddenDeps.env > 0);
|
|
34
|
+
assert.ok(result.hiddenDeps.io > 0);
|
|
35
|
+
});
|
|
36
|
+
it('should detect auth patterns', () => {
|
|
37
|
+
const source = `
|
|
38
|
+
import jwt from 'jsonwebtoken';
|
|
39
|
+
function verifyToken(token) {
|
|
40
|
+
return jwt.verify(token, secret);
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
const result = analyzeBread(source);
|
|
44
|
+
assert.ok(result.authExplicitness > 0);
|
|
45
|
+
assert.ok(result.authPatterns.length > 0);
|
|
46
|
+
});
|
|
47
|
+
// TypeScript 전용 테스트
|
|
48
|
+
it('should detect any type usage', () => {
|
|
49
|
+
const source = `
|
|
50
|
+
function process(data: any) {
|
|
51
|
+
return data.value;
|
|
52
|
+
}
|
|
53
|
+
const x: any = getData();
|
|
54
|
+
`;
|
|
55
|
+
const result = analyzeBread(source);
|
|
56
|
+
assert.ok(result.typeSafety.anyCount >= 2);
|
|
57
|
+
assert.ok(result.typeSafety.safetyScore < 1.0);
|
|
58
|
+
});
|
|
59
|
+
it('should detect type assertions', () => {
|
|
60
|
+
const source = `
|
|
61
|
+
const data = response as UserData;
|
|
62
|
+
const value = input as any;
|
|
63
|
+
`;
|
|
64
|
+
const result = analyzeBread(source);
|
|
65
|
+
assert.ok(result.typeSafety.assertionCount >= 2);
|
|
66
|
+
assert.ok(result.typeSafety.assertionLocations.some(l => l.reason.includes('as any')));
|
|
67
|
+
});
|
|
68
|
+
it('should detect ts-ignore comments', () => {
|
|
69
|
+
const source = `
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
const x = dangerousCall();
|
|
72
|
+
`;
|
|
73
|
+
const result = analyzeBread(source);
|
|
74
|
+
assert.ok(result.typeSafety.tsIgnoreCount >= 1);
|
|
75
|
+
});
|
|
76
|
+
it('should reward unknown type usage', () => {
|
|
77
|
+
const source = `
|
|
78
|
+
function process(data: unknown) {
|
|
79
|
+
if (typeof data === 'string') {
|
|
80
|
+
return data.length;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
const result = analyzeBread(source);
|
|
85
|
+
assert.ok(result.typeSafety.unknownCount >= 1);
|
|
86
|
+
assert.ok(result.typeSafety.safetyScore > 0.9);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('Cheese Analyzer', () => {
|
|
90
|
+
it('should calculate nesting depth', () => {
|
|
91
|
+
const source = `
|
|
92
|
+
function deep() {
|
|
93
|
+
if (a) {
|
|
94
|
+
for (let i = 0; i < 10; i++) {
|
|
95
|
+
while (true) {
|
|
96
|
+
try {
|
|
97
|
+
doSomething();
|
|
98
|
+
} catch (e) {
|
|
99
|
+
console.log(e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
const result = analyzeCheese(source);
|
|
107
|
+
assert.ok(result.maxNesting >= 4);
|
|
108
|
+
});
|
|
109
|
+
it('should detect state×async×retry violation', () => {
|
|
110
|
+
const source = `
|
|
111
|
+
class Service {
|
|
112
|
+
async fetchWithRetry() {
|
|
113
|
+
this.count = 0;
|
|
114
|
+
for (let attempts = 0; attempts < 3; attempts++) {
|
|
115
|
+
try {
|
|
116
|
+
const result = await fetch('/api');
|
|
117
|
+
this.count++;
|
|
118
|
+
return result;
|
|
119
|
+
} catch (e) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
const result = analyzeCheese(source);
|
|
127
|
+
assert.ok(result.stateAsyncRetry.violated);
|
|
128
|
+
assert.ok(result.stateAsyncRetry.count >= 2);
|
|
129
|
+
});
|
|
130
|
+
it('should detect rest parameter anti-pattern', () => {
|
|
131
|
+
const source = `
|
|
132
|
+
function process(...args: any[]) {
|
|
133
|
+
return args.map(x => x * 2);
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
const result = analyzeCheese(source);
|
|
137
|
+
const hasRestPenalty = result.functions.some(f => f.antiPatterns.some(ap => ap.pattern === 'rest_params'));
|
|
138
|
+
assert.ok(hasRestPenalty);
|
|
139
|
+
});
|
|
140
|
+
it('should pass for simple accessible code', () => {
|
|
141
|
+
const source = `
|
|
142
|
+
function add(a: number, b: number): number {
|
|
143
|
+
return a + b;
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
const result = analyzeCheese(source);
|
|
147
|
+
assert.ok(result.accessible);
|
|
148
|
+
assert.strictEqual(result.violations.length, 0);
|
|
149
|
+
});
|
|
150
|
+
// TypeScript 전용 테스트
|
|
151
|
+
it('should detect generic complexity', () => {
|
|
152
|
+
const source = `
|
|
153
|
+
type Complex<T extends Record<K, V>, K extends string, V, R extends T> = {
|
|
154
|
+
[P in keyof T]: T[P];
|
|
155
|
+
};
|
|
156
|
+
`;
|
|
157
|
+
const result = analyzeCheese(source);
|
|
158
|
+
assert.ok(result.typeComplexity.generics.count >= 1);
|
|
159
|
+
assert.ok(result.typeComplexity.generics.maxParams >= 4);
|
|
160
|
+
assert.ok(result.typeComplexity.generics.constrainedCount >= 2);
|
|
161
|
+
});
|
|
162
|
+
it('should detect union type complexity', () => {
|
|
163
|
+
const source = `
|
|
164
|
+
type Status = 'pending' | 'active' | 'completed' | 'cancelled' | 'failed' | 'archived';
|
|
165
|
+
`;
|
|
166
|
+
const result = analyzeCheese(source);
|
|
167
|
+
assert.ok(result.typeComplexity.unions.count >= 1);
|
|
168
|
+
assert.ok(result.typeComplexity.unions.maxMembers >= 6);
|
|
169
|
+
});
|
|
170
|
+
it('should detect conditional types', () => {
|
|
171
|
+
const source = `
|
|
172
|
+
type IsArray<T> = T extends Array<infer U> ? U : never;
|
|
173
|
+
type Unwrap<T> = T extends Promise<infer U> ? U : T;
|
|
174
|
+
`;
|
|
175
|
+
const result = analyzeCheese(source);
|
|
176
|
+
assert.ok(result.typeComplexity.conditionalTypes >= 2);
|
|
177
|
+
});
|
|
178
|
+
it('should detect mapped types', () => {
|
|
179
|
+
const source = `
|
|
180
|
+
type Readonly<T> = { readonly [K in keyof T]: T[K] };
|
|
181
|
+
type Partial<T> = { [K in keyof T]?: T[K] };
|
|
182
|
+
`;
|
|
183
|
+
const result = analyzeCheese(source);
|
|
184
|
+
assert.ok(result.typeComplexity.mappedTypes >= 2);
|
|
185
|
+
});
|
|
186
|
+
it('should detect type guards', () => {
|
|
187
|
+
const source = `
|
|
188
|
+
function isString(value: unknown): value is string {
|
|
189
|
+
return typeof value === 'string';
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
const result = analyzeCheese(source);
|
|
193
|
+
assert.ok(result.typeComplexity.typeGuards >= 1);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('Framework Adjustment', () => {
|
|
197
|
+
it('should detect React framework', () => {
|
|
198
|
+
const source = `
|
|
199
|
+
import React, { useState, useEffect } from 'react';
|
|
200
|
+
|
|
201
|
+
function UserProfile({ userId }: Props) {
|
|
202
|
+
const [user, setUser] = useState(null);
|
|
203
|
+
const [loading, setLoading] = useState(true);
|
|
204
|
+
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
fetchUser(userId).then(setUser);
|
|
207
|
+
}, [userId]);
|
|
208
|
+
|
|
209
|
+
return <div>{user?.name}</div>;
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
const result = analyzeCheese(source);
|
|
213
|
+
assert.strictEqual(result.frameworkInfo.detected, 'react');
|
|
214
|
+
assert.ok(result.frameworkInfo.hookCount >= 2);
|
|
215
|
+
});
|
|
216
|
+
it('should apply JSX nesting weight', () => {
|
|
217
|
+
// Note: TypeScript parser needs proper JSX syntax
|
|
218
|
+
const source = `
|
|
219
|
+
import React from 'react';
|
|
220
|
+
|
|
221
|
+
function Component() {
|
|
222
|
+
// Simulating nested structure with nested functions
|
|
223
|
+
return nested1(() => nested2(() => nested3()));
|
|
224
|
+
}
|
|
225
|
+
`;
|
|
226
|
+
const result = analyzeCheese(source);
|
|
227
|
+
// Framework detected as React, adjustments applied
|
|
228
|
+
assert.strictEqual(result.frameworkInfo.detected, 'react');
|
|
229
|
+
assert.ok(result.adjustedNesting <= result.maxNesting);
|
|
230
|
+
});
|
|
231
|
+
it('should apply hook concept weight', () => {
|
|
232
|
+
const source = `
|
|
233
|
+
import { useState, useEffect, useMemo, useCallback } from 'react';
|
|
234
|
+
|
|
235
|
+
function useCustomHook() {
|
|
236
|
+
const [state, setState] = useState(null);
|
|
237
|
+
const [loading, setLoading] = useState(true);
|
|
238
|
+
const [error, setError] = useState(null);
|
|
239
|
+
|
|
240
|
+
useEffect(() => {}, []);
|
|
241
|
+
|
|
242
|
+
const memoized = useMemo(() => state, [state]);
|
|
243
|
+
const callback = useCallback(() => {}, []);
|
|
244
|
+
|
|
245
|
+
return { state, loading, error, memoized, callback };
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
const result = analyzeCheese(source);
|
|
249
|
+
// Hook이 많지만 가중치 적용되어 개념 수 감소
|
|
250
|
+
assert.ok(result.frameworkInfo.hookCount >= 4);
|
|
251
|
+
const hookAdjustment = result.frameworkInfo.adjustments.find(a => a.type === 'hook');
|
|
252
|
+
if (hookAdjustment) {
|
|
253
|
+
assert.ok(hookAdjustment.adjusted < hookAdjustment.original);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
it('should apply chain method weight', () => {
|
|
257
|
+
const source = `
|
|
258
|
+
import React from 'react';
|
|
259
|
+
|
|
260
|
+
function UserList({ users }: Props) {
|
|
261
|
+
const result = users.filter(u => u.active);
|
|
262
|
+
const names = result.map(u => u.name);
|
|
263
|
+
const sorted = names.sort();
|
|
264
|
+
return sorted.join(', ');
|
|
265
|
+
}
|
|
266
|
+
`;
|
|
267
|
+
const result = analyzeCheese(source);
|
|
268
|
+
// At least some chain methods detected
|
|
269
|
+
assert.ok(result.frameworkInfo.chainCount >= 1);
|
|
270
|
+
});
|
|
271
|
+
it('should detect Vue framework', () => {
|
|
272
|
+
const source = `
|
|
273
|
+
import { ref, computed, onMounted } from 'vue';
|
|
274
|
+
|
|
275
|
+
export default {
|
|
276
|
+
setup() {
|
|
277
|
+
const count = ref(0);
|
|
278
|
+
const doubled = computed(() => count.value * 2);
|
|
279
|
+
|
|
280
|
+
onMounted(() => {
|
|
281
|
+
console.log('mounted');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return { count, doubled };
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
`;
|
|
288
|
+
const result = analyzeCheese(source);
|
|
289
|
+
assert.strictEqual(result.frameworkInfo.detected, 'vue');
|
|
290
|
+
});
|
|
291
|
+
it('should not apply weight for non-framework code', () => {
|
|
292
|
+
const source = `
|
|
293
|
+
function calculate(a: number, b: number) {
|
|
294
|
+
if (a > 0) {
|
|
295
|
+
for (let i = 0; i < b; i++) {
|
|
296
|
+
console.log(i);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return a + b;
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
const result = analyzeCheese(source);
|
|
303
|
+
assert.strictEqual(result.frameworkInfo.detected, 'none');
|
|
304
|
+
assert.strictEqual(result.maxNesting, result.adjustedNesting);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
describe('Ham Analyzer', () => {
|
|
308
|
+
it('should detect critical paths', () => {
|
|
309
|
+
const source = `
|
|
310
|
+
function processPayment(amount: number) {
|
|
311
|
+
return charge(amount);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function authenticateUser(token: string) {
|
|
315
|
+
return verify(token);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function deleteAccount(userId: string) {
|
|
319
|
+
return remove(userId);
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
const result = analyzeHam(source);
|
|
323
|
+
assert.ok(result.criticalPaths.length >= 2);
|
|
324
|
+
assert.ok(result.criticalPaths.some(cp => cp.category === 'payment'));
|
|
325
|
+
assert.ok(result.criticalPaths.some(cp => cp.category === 'auth'));
|
|
326
|
+
});
|
|
327
|
+
it('should detect test framework', () => {
|
|
328
|
+
const testSource = `
|
|
329
|
+
import { describe, it, expect } from 'vitest';
|
|
330
|
+
|
|
331
|
+
describe('Payment', () => {
|
|
332
|
+
it('should process payment', () => {
|
|
333
|
+
expect(processPayment(100)).toBe(true);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
`;
|
|
337
|
+
const result = analyzeHam('', testSource);
|
|
338
|
+
assert.strictEqual(result.testInfo.framework, 'vitest');
|
|
339
|
+
assert.ok(result.testInfo.testCount > 0);
|
|
340
|
+
});
|
|
341
|
+
it('should calculate coverage', () => {
|
|
342
|
+
const source = `
|
|
343
|
+
function processPayment() {}
|
|
344
|
+
function login() {}
|
|
345
|
+
`;
|
|
346
|
+
const testSource = `
|
|
347
|
+
describe('Payment', () => {
|
|
348
|
+
it('should processPayment', () => {});
|
|
349
|
+
});
|
|
350
|
+
`;
|
|
351
|
+
const result = analyzeHam(source, testSource);
|
|
352
|
+
// processPayment is tested, login is not
|
|
353
|
+
assert.ok(result.goldenTestCoverage >= 0);
|
|
354
|
+
assert.ok(result.goldenTestCoverage <= 1);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
//# sourceMappingURL=analyzers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzers.test.js","sourceRoot":"","sources":["../../src/__tests__/analyzers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gate.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { checkGate, getThresholds, STAGE_POLICIES } from '../gate/index.js';
|
|
7
|
+
// Helper to create mock CheeseResult
|
|
8
|
+
function mockCheese(overrides = {}) {
|
|
9
|
+
return {
|
|
10
|
+
accessible: true,
|
|
11
|
+
reason: 'All conditions met',
|
|
12
|
+
violations: [],
|
|
13
|
+
maxNesting: 2,
|
|
14
|
+
adjustedNesting: 2,
|
|
15
|
+
functions: [],
|
|
16
|
+
hiddenDependencies: 0,
|
|
17
|
+
stateAsyncRetry: {
|
|
18
|
+
hasState: false,
|
|
19
|
+
hasAsync: false,
|
|
20
|
+
hasRetry: false,
|
|
21
|
+
axes: [],
|
|
22
|
+
count: 0,
|
|
23
|
+
violated: false,
|
|
24
|
+
},
|
|
25
|
+
config: {
|
|
26
|
+
nestingThreshold: 4,
|
|
27
|
+
conceptsPerFunction: 9,
|
|
28
|
+
hiddenDepThreshold: 2,
|
|
29
|
+
},
|
|
30
|
+
typeComplexity: {
|
|
31
|
+
generics: { count: 0, maxParams: 0, maxDepth: 0, constrainedCount: 0, penalty: 0 },
|
|
32
|
+
unions: { count: 0, maxMembers: 0, intersectionCount: 0, penalty: 0 },
|
|
33
|
+
conditionalTypes: 0,
|
|
34
|
+
mappedTypes: 0,
|
|
35
|
+
typeGuards: 0,
|
|
36
|
+
decoratorStacks: 0,
|
|
37
|
+
totalPenalty: 0,
|
|
38
|
+
},
|
|
39
|
+
frameworkInfo: {
|
|
40
|
+
detected: 'none',
|
|
41
|
+
config: {
|
|
42
|
+
name: 'none',
|
|
43
|
+
jsxNestingWeight: 1.0,
|
|
44
|
+
hookConceptWeight: 1.0,
|
|
45
|
+
chainMethodWeight: 1.0,
|
|
46
|
+
propsDestructureWeight: 1.0,
|
|
47
|
+
},
|
|
48
|
+
jsxNestingDepth: 0,
|
|
49
|
+
logicNestingDepth: 0,
|
|
50
|
+
hookCount: 0,
|
|
51
|
+
chainCount: 0,
|
|
52
|
+
adjustments: [],
|
|
53
|
+
},
|
|
54
|
+
...overrides,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Helper to create mock HamResult
|
|
58
|
+
function mockHam(overrides = {}) {
|
|
59
|
+
return {
|
|
60
|
+
goldenTestCoverage: 0.9,
|
|
61
|
+
criticalPaths: [],
|
|
62
|
+
untestedCriticalPaths: [],
|
|
63
|
+
testInfo: {
|
|
64
|
+
framework: 'jest',
|
|
65
|
+
testCount: 10,
|
|
66
|
+
describedFunctions: new Set(),
|
|
67
|
+
},
|
|
68
|
+
...overrides,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
describe('Gate Thresholds', () => {
|
|
72
|
+
it('should return different thresholds for each gate type', () => {
|
|
73
|
+
const poc = getThresholds('poc');
|
|
74
|
+
const mvp = getThresholds('mvp');
|
|
75
|
+
const production = getThresholds('production');
|
|
76
|
+
// PoC is most lenient
|
|
77
|
+
assert.ok(poc.nesting_max > mvp.nesting_max);
|
|
78
|
+
assert.ok(poc.golden_test_min < mvp.golden_test_min);
|
|
79
|
+
// Production is strictest
|
|
80
|
+
assert.ok(production.nesting_max < mvp.nesting_max);
|
|
81
|
+
assert.ok(production.golden_test_min > mvp.golden_test_min);
|
|
82
|
+
});
|
|
83
|
+
it('should have correct waiver policies', () => {
|
|
84
|
+
assert.strictEqual(STAGE_POLICIES.poc.waiver_allowed, false);
|
|
85
|
+
assert.strictEqual(STAGE_POLICIES.mvp.waiver_allowed, false);
|
|
86
|
+
assert.strictEqual(STAGE_POLICIES.production.waiver_allowed, true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('Gate Check', () => {
|
|
90
|
+
it('should pass MVP gate for good code', () => {
|
|
91
|
+
const cheese = mockCheese({ maxNesting: 3, hiddenDependencies: 1 });
|
|
92
|
+
const ham = mockHam({ goldenTestCoverage: 0.85 });
|
|
93
|
+
const result = checkGate('mvp', cheese, ham);
|
|
94
|
+
assert.strictEqual(result.passed, true);
|
|
95
|
+
assert.strictEqual(result.violations.length, 0);
|
|
96
|
+
});
|
|
97
|
+
it('should fail MVP gate for deep nesting', () => {
|
|
98
|
+
const cheese = mockCheese({ maxNesting: 6 });
|
|
99
|
+
const ham = mockHam();
|
|
100
|
+
const result = checkGate('mvp', cheese, ham);
|
|
101
|
+
assert.strictEqual(result.passed, false);
|
|
102
|
+
assert.ok(result.violations.some(v => v.rule === 'nesting_max'));
|
|
103
|
+
});
|
|
104
|
+
it('should fail MVP gate for low test coverage', () => {
|
|
105
|
+
const cheese = mockCheese();
|
|
106
|
+
const ham = mockHam({ goldenTestCoverage: 0.5 });
|
|
107
|
+
const result = checkGate('mvp', cheese, ham);
|
|
108
|
+
assert.strictEqual(result.passed, false);
|
|
109
|
+
assert.ok(result.violations.some(v => v.rule === 'golden_test_min'));
|
|
110
|
+
});
|
|
111
|
+
it('should fail for state×async×retry violation', () => {
|
|
112
|
+
const cheese = mockCheese({
|
|
113
|
+
stateAsyncRetry: {
|
|
114
|
+
hasState: true,
|
|
115
|
+
hasAsync: true,
|
|
116
|
+
hasRetry: false,
|
|
117
|
+
axes: ['state', 'async'],
|
|
118
|
+
count: 2,
|
|
119
|
+
violated: true,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const ham = mockHam();
|
|
123
|
+
const result = checkGate('mvp', cheese, ham);
|
|
124
|
+
assert.strictEqual(result.passed, false);
|
|
125
|
+
assert.ok(result.violations.some(v => v.rule === 'state_async_retry'));
|
|
126
|
+
});
|
|
127
|
+
it('should pass PoC gate with relaxed thresholds', () => {
|
|
128
|
+
const cheese = mockCheese({ maxNesting: 5, hiddenDependencies: 2 });
|
|
129
|
+
const ham = mockHam({ goldenTestCoverage: 0.5 });
|
|
130
|
+
const result = checkGate('poc', cheese, ham);
|
|
131
|
+
assert.strictEqual(result.passed, true);
|
|
132
|
+
});
|
|
133
|
+
it('should be strict for production gate', () => {
|
|
134
|
+
const cheese = mockCheese({ maxNesting: 4 }); // OK for MVP, too high for production
|
|
135
|
+
const ham = mockHam({ goldenTestCoverage: 0.85 }); // OK for MVP, too low for production
|
|
136
|
+
const result = checkGate('production', cheese, ham);
|
|
137
|
+
assert.strictEqual(result.passed, false);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=gate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.test.js","sourceRoot":"","sources":["../../src/__tests__/gate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🍞 Bread Analyzer - Security & Trust Boundary (TypeScript)
|
|
3
|
+
*
|
|
4
|
+
* 보안 구조 안정성 분석:
|
|
5
|
+
* - Trust boundary 탐지
|
|
6
|
+
* - Auth/Authz 흐름 분석
|
|
7
|
+
* - Secret 패턴 탐지
|
|
8
|
+
* - Hidden dependencies 계산
|
|
9
|
+
*
|
|
10
|
+
* TypeScript 전용 타입 안전성 분석:
|
|
11
|
+
* - any 타입 사용 탐지
|
|
12
|
+
* - Type assertion (as) 남용
|
|
13
|
+
* - @ts-ignore, @ts-expect-error 사용
|
|
14
|
+
* - Non-null assertion (!) 남용
|
|
15
|
+
* - unknown vs any 사용 패턴
|
|
16
|
+
*/
|
|
17
|
+
export interface BreadResult {
|
|
18
|
+
trustBoundaryCount: number;
|
|
19
|
+
trustBoundaries: TrustBoundary[];
|
|
20
|
+
authExplicitness: number;
|
|
21
|
+
authPatterns: string[];
|
|
22
|
+
secretPatterns: SecretPattern[];
|
|
23
|
+
hiddenDeps: HiddenDeps;
|
|
24
|
+
violations: string[];
|
|
25
|
+
typeSafety: TypeSafetyResult;
|
|
26
|
+
}
|
|
27
|
+
export interface TypeSafetyResult {
|
|
28
|
+
anyCount: number;
|
|
29
|
+
anyLocations: TypeUnsafeLocation[];
|
|
30
|
+
assertionCount: number;
|
|
31
|
+
assertionLocations: TypeUnsafeLocation[];
|
|
32
|
+
nonNullAssertionCount: number;
|
|
33
|
+
tsIgnoreCount: number;
|
|
34
|
+
tsIgnoreLocations: TypeUnsafeLocation[];
|
|
35
|
+
unknownCount: number;
|
|
36
|
+
safetyScore: number;
|
|
37
|
+
}
|
|
38
|
+
export interface TypeUnsafeLocation {
|
|
39
|
+
line: number;
|
|
40
|
+
column: number;
|
|
41
|
+
code: string;
|
|
42
|
+
reason: string;
|
|
43
|
+
}
|
|
44
|
+
export interface TrustBoundary {
|
|
45
|
+
name: string;
|
|
46
|
+
line: number;
|
|
47
|
+
boundaryType: 'marker' | 'api' | 'auth' | 'network' | 'data';
|
|
48
|
+
description: string;
|
|
49
|
+
}
|
|
50
|
+
export interface SecretPattern {
|
|
51
|
+
line: number;
|
|
52
|
+
patternType: string;
|
|
53
|
+
snippet: string;
|
|
54
|
+
severity: 'high' | 'medium' | 'low';
|
|
55
|
+
}
|
|
56
|
+
export interface HiddenDeps {
|
|
57
|
+
global: number;
|
|
58
|
+
env: number;
|
|
59
|
+
io: number;
|
|
60
|
+
total: number;
|
|
61
|
+
}
|
|
62
|
+
export declare function analyzeBread(source: string, _filePath?: string): BreadResult;
|
|
63
|
+
//# sourceMappingURL=bread.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bread.d.ts","sourceRoot":"","sources":["../../src/analyzers/bread.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IAGrB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;IACzC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAgJD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CA6B5E"}
|