technical-debt-radar 1.16.0 → 1.17.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 +1 -1
- package/dist/index.js +992 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Stop Node.js production crashes before merge.**
|
|
4
4
|
|
|
5
|
-
Detects event-loop blockers, dangerous ORM patterns, security issues, and architecture drift in your PRs.
|
|
5
|
+
Detects event-loop blockers, dangerous ORM patterns, security issues, and architecture drift in your PRs. 74 deterministic detection rules across 6 categories. Works with NestJS, Express, Fastify, Koa, Hapi + 7 ORMs.
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
package/dist/index.js
CHANGED
|
@@ -39,7 +39,7 @@ var require_constants = __commonJS({
|
|
|
39
39
|
"../../packages/shared/dist/constants/index.js"(exports2) {
|
|
40
40
|
"use strict";
|
|
41
41
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
42
|
-
exports2.DEBT_DELTA_WARN_THRESHOLD = exports2.DEBT_DELTA_BLOCK_THRESHOLD = exports2.DEFAULT_LARGE_FILE_THRESHOLD = exports2.DEFAULT_COMPLEXITY_THRESHOLD = exports2.MAX_CHANGED_FILES_PER_PR = exports2.FIRST_SCAN_TIMEOUT_MS = exports2.PR_ANALYSIS_TIMEOUT_MS = exports2.MAX_AI_OUTPUT_TOKENS = exports2.MAX_AI_TOKENS_PER_FUNCTION = exports2.MAX_SUSPECT_FUNCTIONS_PER_PR = exports2.MAX_CALLER_TRACE_DEPTH = exports2.MAX_CROSS_FILE_SUSPECTS = exports2.SECURITY_RULES = exports2.CROSS_FILE_RULES = exports2.ARCHITECTURE_RULES = exports2.MAINTAINABILITY_RULES = exports2.PERFORMANCE_RULES = exports2.RELIABILITY_RULES = exports2.RUNTIME_RISK_RULES = exports2.VOLUME_THRESHOLDS = exports2.DEFAULT_SCORING = exports2.DEFAULT_MODE = void 0;
|
|
42
|
+
exports2.DEBT_DELTA_WARN_THRESHOLD = exports2.DEBT_DELTA_BLOCK_THRESHOLD = exports2.DEFAULT_NESTING_DEPTH_THRESHOLD = exports2.DEFAULT_PARAMETER_COUNT_THRESHOLD = exports2.DEFAULT_FUNCTION_LENGTH_THRESHOLD = exports2.DEFAULT_LARGE_FILE_THRESHOLD = exports2.DEFAULT_COMPLEXITY_THRESHOLD = exports2.MAX_CHANGED_FILES_PER_PR = exports2.FIRST_SCAN_TIMEOUT_MS = exports2.PR_ANALYSIS_TIMEOUT_MS = exports2.MAX_AI_OUTPUT_TOKENS = exports2.MAX_AI_TOKENS_PER_FUNCTION = exports2.MAX_SUSPECT_FUNCTIONS_PER_PR = exports2.MAX_CALLER_TRACE_DEPTH = exports2.MAX_CROSS_FILE_SUSPECTS = exports2.SECURITY_RULES = exports2.CROSS_FILE_RULES = exports2.ARCHITECTURE_RULES = exports2.MAINTAINABILITY_RULES = exports2.PERFORMANCE_RULES = exports2.RELIABILITY_RULES = exports2.RUNTIME_RISK_RULES = exports2.VOLUME_THRESHOLDS = exports2.DEFAULT_SCORING = exports2.DEFAULT_MODE = void 0;
|
|
43
43
|
exports2.DEFAULT_MODE = "warn";
|
|
44
44
|
exports2.DEFAULT_SCORING = {
|
|
45
45
|
architecture_violation: 5,
|
|
@@ -119,7 +119,13 @@ var require_constants = __commonJS({
|
|
|
119
119
|
DEBUG_CONSOLE_LOG: "debug-console-log-in-production",
|
|
120
120
|
LOW_TEST_COVERAGE: "low-test-coverage",
|
|
121
121
|
LOW_BRANCH_COVERAGE: "low-branch-coverage",
|
|
122
|
-
COVERAGE_DROP: "coverage-drop"
|
|
122
|
+
COVERAGE_DROP: "coverage-drop",
|
|
123
|
+
PROCESS_ENV_TYPO: "process-env-typo",
|
|
124
|
+
FUNCTION_TOO_LONG: "function-too-long",
|
|
125
|
+
TOO_MANY_PARAMETERS: "too-many-parameters",
|
|
126
|
+
DEEP_NESTING: "deep-nesting",
|
|
127
|
+
CONSOLE_LOG_IN_PRODUCTION: "console-log-in-production",
|
|
128
|
+
STRAY_TODO_COMMENT: "stray-todo-comment"
|
|
123
129
|
};
|
|
124
130
|
exports2.ARCHITECTURE_RULES = {
|
|
125
131
|
LAYER_VIOLATION: "layer-boundary-violation",
|
|
@@ -142,7 +148,10 @@ var require_constants = __commonJS({
|
|
|
142
148
|
HARDCODED_SECRET_LITERAL: "hardcoded-secret-literal",
|
|
143
149
|
EVAL_USAGE: "eval-usage",
|
|
144
150
|
MATH_RANDOM_FOR_SECURITY: "math-random-for-security",
|
|
145
|
-
TLS_VALIDATION_DISABLED: "tls-validation-disabled"
|
|
151
|
+
TLS_VALIDATION_DISABLED: "tls-validation-disabled",
|
|
152
|
+
CORS_WILDCARD_CREDENTIALS: "cors-wildcard-credentials",
|
|
153
|
+
JWT_INSECURE_OPTIONS: "jwt-insecure-options",
|
|
154
|
+
DANGEROUSLY_SET_INNER_HTML_UNSANITIZED: "dangerously-set-inner-html-unsanitized"
|
|
146
155
|
};
|
|
147
156
|
exports2.MAX_CROSS_FILE_SUSPECTS = 10;
|
|
148
157
|
exports2.MAX_CALLER_TRACE_DEPTH = 2;
|
|
@@ -154,6 +163,9 @@ var require_constants = __commonJS({
|
|
|
154
163
|
exports2.MAX_CHANGED_FILES_PER_PR = 100;
|
|
155
164
|
exports2.DEFAULT_COMPLEXITY_THRESHOLD = 10;
|
|
156
165
|
exports2.DEFAULT_LARGE_FILE_THRESHOLD = 300;
|
|
166
|
+
exports2.DEFAULT_FUNCTION_LENGTH_THRESHOLD = 50;
|
|
167
|
+
exports2.DEFAULT_PARAMETER_COUNT_THRESHOLD = 4;
|
|
168
|
+
exports2.DEFAULT_NESTING_DEPTH_THRESHOLD = 4;
|
|
157
169
|
exports2.DEBT_DELTA_BLOCK_THRESHOLD = 15;
|
|
158
170
|
exports2.DEBT_DELTA_WARN_THRESHOLD = 8;
|
|
159
171
|
}
|
|
@@ -1058,6 +1070,12 @@ var require_parser = __commonJS({
|
|
|
1058
1070
|
}
|
|
1059
1071
|
result.ignore_tests_for = m.ignore_tests_for.map(String);
|
|
1060
1072
|
}
|
|
1073
|
+
if (m.check_env_drift !== void 0) {
|
|
1074
|
+
if (typeof m.check_env_drift !== "boolean") {
|
|
1075
|
+
throw new PolicyValidationError("maintainability.check_env_drift must be a boolean");
|
|
1076
|
+
}
|
|
1077
|
+
result.check_env_drift = m.check_env_drift;
|
|
1078
|
+
}
|
|
1061
1079
|
return result;
|
|
1062
1080
|
}
|
|
1063
1081
|
function parseScoring(value) {
|
|
@@ -7714,7 +7732,8 @@ var require_validator = __commonJS({
|
|
|
7714
7732
|
properties: {
|
|
7715
7733
|
require_tests: { type: "boolean" },
|
|
7716
7734
|
require_tests_for: { type: "array", items: { type: "string" } },
|
|
7717
|
-
ignore_tests_for: { type: "array", items: { type: "string" } }
|
|
7735
|
+
ignore_tests_for: { type: "array", items: { type: "string" } },
|
|
7736
|
+
check_env_drift: { type: "boolean" }
|
|
7718
7737
|
},
|
|
7719
7738
|
additionalProperties: false
|
|
7720
7739
|
}
|
|
@@ -20372,6 +20391,919 @@ var require_tls_detector = __commonJS({
|
|
|
20372
20391
|
}
|
|
20373
20392
|
});
|
|
20374
20393
|
|
|
20394
|
+
// ../../packages/analyzers/dist/typescript/parsed-files.js
|
|
20395
|
+
var require_parsed_files = __commonJS({
|
|
20396
|
+
"../../packages/analyzers/dist/typescript/parsed-files.js"(exports2) {
|
|
20397
|
+
"use strict";
|
|
20398
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20399
|
+
exports2.parseChangedFiles = parseChangedFiles;
|
|
20400
|
+
var ts_morph_1 = require("ts-morph");
|
|
20401
|
+
function parseChangedFiles(input) {
|
|
20402
|
+
const project = new ts_morph_1.Project({ useInMemoryFileSystem: true });
|
|
20403
|
+
const parsed = [];
|
|
20404
|
+
for (const file of input.changedFiles) {
|
|
20405
|
+
if (file.status === "deleted")
|
|
20406
|
+
continue;
|
|
20407
|
+
if (!/\.(ts|tsx|js|jsx)$/.test(file.path))
|
|
20408
|
+
continue;
|
|
20409
|
+
parsed.push({
|
|
20410
|
+
path: file.path,
|
|
20411
|
+
sourceFile: project.createSourceFile(file.path, file.content)
|
|
20412
|
+
});
|
|
20413
|
+
}
|
|
20414
|
+
return parsed;
|
|
20415
|
+
}
|
|
20416
|
+
}
|
|
20417
|
+
});
|
|
20418
|
+
|
|
20419
|
+
// ../../packages/analyzers/dist/typescript/cors-detector.js
|
|
20420
|
+
var require_cors_detector = __commonJS({
|
|
20421
|
+
"../../packages/analyzers/dist/typescript/cors-detector.js"(exports2) {
|
|
20422
|
+
"use strict";
|
|
20423
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20424
|
+
exports2.detectCorsWildcardCredentials = detectCorsWildcardCredentials;
|
|
20425
|
+
var shared_1 = require_dist();
|
|
20426
|
+
var ts_morph_1 = require("ts-morph");
|
|
20427
|
+
var parsed_files_1 = require_parsed_files();
|
|
20428
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.fixture\.|\.example\.|\.stories\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
20429
|
+
async function detectCorsWildcardCredentials(input, policy, parsed) {
|
|
20430
|
+
const violations = [];
|
|
20431
|
+
for (const { path: path9, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
20432
|
+
if (EXCLUDED_PATH_REGEX.test(path9))
|
|
20433
|
+
continue;
|
|
20434
|
+
scanFile(sourceFile, path9, policy, violations);
|
|
20435
|
+
}
|
|
20436
|
+
return violations;
|
|
20437
|
+
}
|
|
20438
|
+
function scanFile(sourceFile, filePath, policy, violations) {
|
|
20439
|
+
sourceFile.forEachDescendant((node) => {
|
|
20440
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
20441
|
+
return;
|
|
20442
|
+
const options = corsOptionsObject(node);
|
|
20443
|
+
if (!options)
|
|
20444
|
+
return;
|
|
20445
|
+
if (!hasDangerousCorsCombo(options))
|
|
20446
|
+
return;
|
|
20447
|
+
violations.push({
|
|
20448
|
+
category: "runtime_risk",
|
|
20449
|
+
type: shared_1.SECURITY_RULES.CORS_WILDCARD_CREDENTIALS,
|
|
20450
|
+
ruleId: shared_1.SECURITY_RULES.CORS_WILDCARD_CREDENTIALS,
|
|
20451
|
+
severity: "critical",
|
|
20452
|
+
source: "deterministic",
|
|
20453
|
+
confidence: "high",
|
|
20454
|
+
file: filePath,
|
|
20455
|
+
line: node.getStartLineNumber(),
|
|
20456
|
+
function: getEnclosingFunctionName(node),
|
|
20457
|
+
message: "CORS is configured with a wildcard/reflected origin and credentials: true \u2014 any website can make authenticated cross-origin requests to this API.",
|
|
20458
|
+
suggestion: "When credentials are enabled, set `origin` to an explicit allow-list of trusted domains. A wildcard (or reflected) origin cannot be combined with credentials.",
|
|
20459
|
+
debtPoints: policy.scoring.runtime_risk_critical,
|
|
20460
|
+
gateAction: "block"
|
|
20461
|
+
});
|
|
20462
|
+
});
|
|
20463
|
+
}
|
|
20464
|
+
function corsOptionsObject(call) {
|
|
20465
|
+
const expr = call.getExpression();
|
|
20466
|
+
const args = call.getArguments();
|
|
20467
|
+
if (ts_morph_1.Node.isIdentifier(expr) && expr.getText() === "cors") {
|
|
20468
|
+
return objectLiteralArg(args, 0);
|
|
20469
|
+
}
|
|
20470
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(expr)) {
|
|
20471
|
+
const method = expr.getName();
|
|
20472
|
+
if (method === "enableCors") {
|
|
20473
|
+
return objectLiteralArg(args, 0);
|
|
20474
|
+
}
|
|
20475
|
+
if (method === "register" && argsReferenceCors(args)) {
|
|
20476
|
+
for (const arg of args) {
|
|
20477
|
+
if (ts_morph_1.Node.isObjectLiteralExpression(arg))
|
|
20478
|
+
return arg;
|
|
20479
|
+
}
|
|
20480
|
+
}
|
|
20481
|
+
}
|
|
20482
|
+
return void 0;
|
|
20483
|
+
}
|
|
20484
|
+
function objectLiteralArg(args, index) {
|
|
20485
|
+
const arg = args[index];
|
|
20486
|
+
return arg && ts_morph_1.Node.isObjectLiteralExpression(arg) ? arg : void 0;
|
|
20487
|
+
}
|
|
20488
|
+
function argsReferenceCors(args) {
|
|
20489
|
+
return args.some((a) => /cors/i.test(a.getText()));
|
|
20490
|
+
}
|
|
20491
|
+
function hasDangerousCorsCombo(obj) {
|
|
20492
|
+
let originIsWildcard = false;
|
|
20493
|
+
let credentialsEnabled = false;
|
|
20494
|
+
for (const prop of obj.getProperties()) {
|
|
20495
|
+
if (!ts_morph_1.Node.isPropertyAssignment(prop))
|
|
20496
|
+
continue;
|
|
20497
|
+
const name = prop.getName().replace(/['"`]/g, "");
|
|
20498
|
+
const init = prop.getInitializer();
|
|
20499
|
+
if (!init)
|
|
20500
|
+
continue;
|
|
20501
|
+
if (name === "origin" && isWildcardOrigin(init)) {
|
|
20502
|
+
originIsWildcard = true;
|
|
20503
|
+
} else if (name === "credentials" && init.getText().trim() === "true") {
|
|
20504
|
+
credentialsEnabled = true;
|
|
20505
|
+
}
|
|
20506
|
+
}
|
|
20507
|
+
return originIsWildcard && credentialsEnabled;
|
|
20508
|
+
}
|
|
20509
|
+
function isWildcardOrigin(init) {
|
|
20510
|
+
if (ts_morph_1.Node.isStringLiteral(init))
|
|
20511
|
+
return init.getLiteralValue() === "*";
|
|
20512
|
+
if (init.getText().trim() === "true")
|
|
20513
|
+
return true;
|
|
20514
|
+
if (ts_morph_1.Node.isArrayLiteralExpression(init)) {
|
|
20515
|
+
return init.getElements().some((el) => ts_morph_1.Node.isStringLiteral(el) && el.getLiteralValue() === "*");
|
|
20516
|
+
}
|
|
20517
|
+
return false;
|
|
20518
|
+
}
|
|
20519
|
+
function getEnclosingFunctionName(node) {
|
|
20520
|
+
const fn = node.getFirstAncestor((a) => ts_morph_1.Node.isFunctionDeclaration(a) || ts_morph_1.Node.isMethodDeclaration(a));
|
|
20521
|
+
if (fn && (ts_morph_1.Node.isFunctionDeclaration(fn) || ts_morph_1.Node.isMethodDeclaration(fn))) {
|
|
20522
|
+
return fn.getName() ?? void 0;
|
|
20523
|
+
}
|
|
20524
|
+
return void 0;
|
|
20525
|
+
}
|
|
20526
|
+
}
|
|
20527
|
+
});
|
|
20528
|
+
|
|
20529
|
+
// ../../packages/analyzers/dist/typescript/jwt-detector.js
|
|
20530
|
+
var require_jwt_detector = __commonJS({
|
|
20531
|
+
"../../packages/analyzers/dist/typescript/jwt-detector.js"(exports2) {
|
|
20532
|
+
"use strict";
|
|
20533
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20534
|
+
exports2.detectJwtInsecureOptions = detectJwtInsecureOptions;
|
|
20535
|
+
var shared_1 = require_dist();
|
|
20536
|
+
var ts_morph_1 = require("ts-morph");
|
|
20537
|
+
var parsed_files_1 = require_parsed_files();
|
|
20538
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.fixture\.|\.example\.|\.stories\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
20539
|
+
var MIN_SECRET_LENGTH = 32;
|
|
20540
|
+
var SIGN_METHODS = /* @__PURE__ */ new Set(["sign", "signAsync"]);
|
|
20541
|
+
var VERIFY_METHODS = /* @__PURE__ */ new Set(["verify", "verifyAsync"]);
|
|
20542
|
+
async function detectJwtInsecureOptions(input, policy, parsed) {
|
|
20543
|
+
const violations = [];
|
|
20544
|
+
for (const { path: path9, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
20545
|
+
if (EXCLUDED_PATH_REGEX.test(path9))
|
|
20546
|
+
continue;
|
|
20547
|
+
scanFile(sourceFile, path9, policy, violations);
|
|
20548
|
+
}
|
|
20549
|
+
return violations;
|
|
20550
|
+
}
|
|
20551
|
+
function scanFile(sourceFile, filePath, policy, violations) {
|
|
20552
|
+
sourceFile.forEachDescendant((node) => {
|
|
20553
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
20554
|
+
return;
|
|
20555
|
+
const call = jwtCallKind(node);
|
|
20556
|
+
if (!call)
|
|
20557
|
+
return;
|
|
20558
|
+
const { kind, style } = call;
|
|
20559
|
+
const args = node.getArguments();
|
|
20560
|
+
const line = node.getStartLineNumber();
|
|
20561
|
+
const fn = getEnclosingFunctionName(node);
|
|
20562
|
+
const secretArg = style === "lib" ? args[1] : void 0;
|
|
20563
|
+
const optionsArg = style === "lib" ? args[2] : args[1];
|
|
20564
|
+
const optionsObj = optionsArg && ts_morph_1.Node.isObjectLiteralExpression(optionsArg) ? optionsArg : void 0;
|
|
20565
|
+
if (optionsObj && hasAlgorithmNone(optionsObj)) {
|
|
20566
|
+
violations.push({
|
|
20567
|
+
category: "runtime_risk",
|
|
20568
|
+
type: shared_1.SECURITY_RULES.JWT_INSECURE_OPTIONS,
|
|
20569
|
+
ruleId: shared_1.SECURITY_RULES.JWT_INSECURE_OPTIONS,
|
|
20570
|
+
severity: "critical",
|
|
20571
|
+
source: "deterministic",
|
|
20572
|
+
confidence: "high",
|
|
20573
|
+
file: filePath,
|
|
20574
|
+
line,
|
|
20575
|
+
function: fn,
|
|
20576
|
+
message: "JWT is configured with the 'none' algorithm \u2014 the token signature is disabled, so any forged token is accepted.",
|
|
20577
|
+
suggestion: "Pin an explicit signing algorithm (e.g. algorithm: 'HS256' or 'RS256') and never include 'none' in the algorithms allow-list.",
|
|
20578
|
+
debtPoints: policy.scoring.runtime_risk_critical,
|
|
20579
|
+
gateAction: "block"
|
|
20580
|
+
});
|
|
20581
|
+
}
|
|
20582
|
+
if (kind === "sign" && !hasExpiresIn(optionsArg, optionsObj)) {
|
|
20583
|
+
violations.push({
|
|
20584
|
+
category: "runtime_risk",
|
|
20585
|
+
type: shared_1.SECURITY_RULES.JWT_INSECURE_OPTIONS,
|
|
20586
|
+
ruleId: shared_1.SECURITY_RULES.JWT_INSECURE_OPTIONS,
|
|
20587
|
+
severity: "warning",
|
|
20588
|
+
source: "deterministic",
|
|
20589
|
+
confidence: "high",
|
|
20590
|
+
file: filePath,
|
|
20591
|
+
line,
|
|
20592
|
+
function: fn,
|
|
20593
|
+
message: "JWT is signed without an `expiresIn` option \u2014 the issued token never expires and cannot be aged out.",
|
|
20594
|
+
suggestion: "Pass an explicit lifetime, e.g. sign(payload, secret, { expiresIn: '15m' }).",
|
|
20595
|
+
debtPoints: policy.scoring.runtime_risk_warning,
|
|
20596
|
+
gateAction: "warn"
|
|
20597
|
+
});
|
|
20598
|
+
}
|
|
20599
|
+
const secretLen = literalSecretLength(secretArg);
|
|
20600
|
+
if (secretLen !== void 0 && secretLen < MIN_SECRET_LENGTH) {
|
|
20601
|
+
violations.push({
|
|
20602
|
+
category: "runtime_risk",
|
|
20603
|
+
type: shared_1.SECURITY_RULES.JWT_INSECURE_OPTIONS,
|
|
20604
|
+
ruleId: shared_1.SECURITY_RULES.JWT_INSECURE_OPTIONS,
|
|
20605
|
+
severity: "warning",
|
|
20606
|
+
source: "deterministic",
|
|
20607
|
+
confidence: "high",
|
|
20608
|
+
file: filePath,
|
|
20609
|
+
line,
|
|
20610
|
+
function: fn,
|
|
20611
|
+
message: `JWT secret is a ${secretLen}-character string literal \u2014 HMAC secrets shorter than ${MIN_SECRET_LENGTH} characters are brute-forceable, and a literal secret is also hardcoded.`,
|
|
20612
|
+
suggestion: 'Load the secret from an environment variable and use at least 32 random characters (e.g. `crypto.randomBytes(32).toString("hex")`).',
|
|
20613
|
+
debtPoints: policy.scoring.runtime_risk_warning,
|
|
20614
|
+
gateAction: "warn"
|
|
20615
|
+
});
|
|
20616
|
+
}
|
|
20617
|
+
});
|
|
20618
|
+
}
|
|
20619
|
+
function jwtCallKind(call) {
|
|
20620
|
+
const expr = call.getExpression();
|
|
20621
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
20622
|
+
return void 0;
|
|
20623
|
+
const method = expr.getName();
|
|
20624
|
+
const isSign = SIGN_METHODS.has(method);
|
|
20625
|
+
const isVerify = VERIFY_METHODS.has(method);
|
|
20626
|
+
if (!isSign && !isVerify)
|
|
20627
|
+
return void 0;
|
|
20628
|
+
const receiver = expr.getExpression().getText();
|
|
20629
|
+
if (!/jwt|jsonwebtoken/i.test(receiver))
|
|
20630
|
+
return void 0;
|
|
20631
|
+
return {
|
|
20632
|
+
kind: isSign ? "sign" : "verify",
|
|
20633
|
+
style: /jwtService/i.test(receiver) ? "service" : "lib"
|
|
20634
|
+
};
|
|
20635
|
+
}
|
|
20636
|
+
function hasAlgorithmNone(obj) {
|
|
20637
|
+
for (const prop of obj.getProperties()) {
|
|
20638
|
+
if (!ts_morph_1.Node.isPropertyAssignment(prop))
|
|
20639
|
+
continue;
|
|
20640
|
+
const name = prop.getName().replace(/['"`]/g, "");
|
|
20641
|
+
if (name !== "algorithm" && name !== "algorithms")
|
|
20642
|
+
continue;
|
|
20643
|
+
const init = prop.getInitializer();
|
|
20644
|
+
if (!init)
|
|
20645
|
+
continue;
|
|
20646
|
+
if (isNoneLiteral(init))
|
|
20647
|
+
return true;
|
|
20648
|
+
if (ts_morph_1.Node.isArrayLiteralExpression(init)) {
|
|
20649
|
+
if (init.getElements().some(isNoneLiteral))
|
|
20650
|
+
return true;
|
|
20651
|
+
}
|
|
20652
|
+
}
|
|
20653
|
+
return false;
|
|
20654
|
+
}
|
|
20655
|
+
function isNoneLiteral(node) {
|
|
20656
|
+
if (ts_morph_1.Node.isStringLiteral(node) || ts_morph_1.Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
20657
|
+
return node.getLiteralValue().toLowerCase() === "none";
|
|
20658
|
+
}
|
|
20659
|
+
return false;
|
|
20660
|
+
}
|
|
20661
|
+
function hasExpiresIn(optionsArg, optionsObj) {
|
|
20662
|
+
if (!optionsArg)
|
|
20663
|
+
return false;
|
|
20664
|
+
if (!optionsObj)
|
|
20665
|
+
return true;
|
|
20666
|
+
return optionsObj.getProperties().some((prop) => {
|
|
20667
|
+
if (ts_morph_1.Node.isPropertyAssignment(prop) || ts_morph_1.Node.isShorthandPropertyAssignment(prop)) {
|
|
20668
|
+
return prop.getName().replace(/['"`]/g, "") === "expiresIn";
|
|
20669
|
+
}
|
|
20670
|
+
return false;
|
|
20671
|
+
});
|
|
20672
|
+
}
|
|
20673
|
+
function literalSecretLength(node) {
|
|
20674
|
+
if (!node)
|
|
20675
|
+
return void 0;
|
|
20676
|
+
if (ts_morph_1.Node.isStringLiteral(node) || ts_morph_1.Node.isNoSubstitutionTemplateLiteral(node)) {
|
|
20677
|
+
return node.getLiteralValue().length;
|
|
20678
|
+
}
|
|
20679
|
+
return void 0;
|
|
20680
|
+
}
|
|
20681
|
+
function getEnclosingFunctionName(node) {
|
|
20682
|
+
const fn = node.getFirstAncestor((a) => ts_morph_1.Node.isFunctionDeclaration(a) || ts_morph_1.Node.isMethodDeclaration(a));
|
|
20683
|
+
if (fn && (ts_morph_1.Node.isFunctionDeclaration(fn) || ts_morph_1.Node.isMethodDeclaration(fn))) {
|
|
20684
|
+
return fn.getName() ?? void 0;
|
|
20685
|
+
}
|
|
20686
|
+
return void 0;
|
|
20687
|
+
}
|
|
20688
|
+
}
|
|
20689
|
+
});
|
|
20690
|
+
|
|
20691
|
+
// ../../packages/analyzers/dist/typescript/jsx-xss-detector.js
|
|
20692
|
+
var require_jsx_xss_detector = __commonJS({
|
|
20693
|
+
"../../packages/analyzers/dist/typescript/jsx-xss-detector.js"(exports2) {
|
|
20694
|
+
"use strict";
|
|
20695
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20696
|
+
exports2.detectDangerouslySetInnerHtml = detectDangerouslySetInnerHtml;
|
|
20697
|
+
var shared_1 = require_dist();
|
|
20698
|
+
var ts_morph_1 = require("ts-morph");
|
|
20699
|
+
var parsed_files_1 = require_parsed_files();
|
|
20700
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.fixture\.|\.stories\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
20701
|
+
var SANITIZER_NAME_REGEX = /^(sanitize|sanitizeHtml|xss|escape|escapeHtml|striptags|purify)$/i;
|
|
20702
|
+
async function detectDangerouslySetInnerHtml(input, policy, parsed) {
|
|
20703
|
+
const violations = [];
|
|
20704
|
+
for (const { path: path9, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
20705
|
+
if (!/\.(tsx|jsx)$/.test(path9))
|
|
20706
|
+
continue;
|
|
20707
|
+
if (EXCLUDED_PATH_REGEX.test(path9))
|
|
20708
|
+
continue;
|
|
20709
|
+
scanFile(sourceFile, path9, policy, violations);
|
|
20710
|
+
}
|
|
20711
|
+
return violations;
|
|
20712
|
+
}
|
|
20713
|
+
function scanFile(sourceFile, filePath, policy, violations) {
|
|
20714
|
+
sourceFile.forEachDescendant((node) => {
|
|
20715
|
+
if (!ts_morph_1.Node.isJsxAttribute(node))
|
|
20716
|
+
return;
|
|
20717
|
+
if (node.getNameNode().getText() !== "dangerouslySetInnerHTML")
|
|
20718
|
+
return;
|
|
20719
|
+
const htmlValue = extractHtmlValue(node);
|
|
20720
|
+
if (!htmlValue)
|
|
20721
|
+
return;
|
|
20722
|
+
if (isSafeHtmlValue(htmlValue))
|
|
20723
|
+
return;
|
|
20724
|
+
violations.push({
|
|
20725
|
+
category: "runtime_risk",
|
|
20726
|
+
type: shared_1.SECURITY_RULES.DANGEROUSLY_SET_INNER_HTML_UNSANITIZED,
|
|
20727
|
+
ruleId: shared_1.SECURITY_RULES.DANGEROUSLY_SET_INNER_HTML_UNSANITIZED,
|
|
20728
|
+
severity: "warning",
|
|
20729
|
+
source: "deterministic",
|
|
20730
|
+
confidence: "medium",
|
|
20731
|
+
file: filePath,
|
|
20732
|
+
line: node.getStartLineNumber(),
|
|
20733
|
+
message: "dangerouslySetInnerHTML is set from a value that is not a static string or a sanitizer call \u2014 untrusted HTML here is a cross-site scripting (XSS) sink.",
|
|
20734
|
+
suggestion: "Sanitize the HTML before insertion, e.g. dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(value) }}, or render the value as text instead of HTML.",
|
|
20735
|
+
debtPoints: policy.scoring.runtime_risk_warning,
|
|
20736
|
+
gateAction: "warn"
|
|
20737
|
+
});
|
|
20738
|
+
});
|
|
20739
|
+
}
|
|
20740
|
+
function extractHtmlValue(attr) {
|
|
20741
|
+
if (!ts_morph_1.Node.isJsxAttribute(attr))
|
|
20742
|
+
return void 0;
|
|
20743
|
+
const init = attr.getInitializer();
|
|
20744
|
+
if (!init || !ts_morph_1.Node.isJsxExpression(init))
|
|
20745
|
+
return void 0;
|
|
20746
|
+
const expr = init.getExpression();
|
|
20747
|
+
if (!expr || !ts_morph_1.Node.isObjectLiteralExpression(expr))
|
|
20748
|
+
return void 0;
|
|
20749
|
+
for (const prop of expr.getProperties()) {
|
|
20750
|
+
if (ts_morph_1.Node.isPropertyAssignment(prop)) {
|
|
20751
|
+
if (prop.getName().replace(/['"`]/g, "") === "__html") {
|
|
20752
|
+
return prop.getInitializer();
|
|
20753
|
+
}
|
|
20754
|
+
} else if (ts_morph_1.Node.isShorthandPropertyAssignment(prop)) {
|
|
20755
|
+
if (prop.getName() === "__html") {
|
|
20756
|
+
return prop.getNameNode();
|
|
20757
|
+
}
|
|
20758
|
+
}
|
|
20759
|
+
}
|
|
20760
|
+
return void 0;
|
|
20761
|
+
}
|
|
20762
|
+
function isSafeHtmlValue(value) {
|
|
20763
|
+
if (ts_morph_1.Node.isStringLiteral(value) || ts_morph_1.Node.isNoSubstitutionTemplateLiteral(value)) {
|
|
20764
|
+
return true;
|
|
20765
|
+
}
|
|
20766
|
+
if (ts_morph_1.Node.isCallExpression(value)) {
|
|
20767
|
+
return isSanitizerCall(value);
|
|
20768
|
+
}
|
|
20769
|
+
return false;
|
|
20770
|
+
}
|
|
20771
|
+
function isSanitizerCall(call) {
|
|
20772
|
+
if (!ts_morph_1.Node.isCallExpression(call))
|
|
20773
|
+
return false;
|
|
20774
|
+
const expr = call.getExpression();
|
|
20775
|
+
let name;
|
|
20776
|
+
if (ts_morph_1.Node.isIdentifier(expr)) {
|
|
20777
|
+
name = expr.getText();
|
|
20778
|
+
} else if (ts_morph_1.Node.isPropertyAccessExpression(expr)) {
|
|
20779
|
+
name = expr.getName();
|
|
20780
|
+
}
|
|
20781
|
+
return name !== void 0 && SANITIZER_NAME_REGEX.test(name);
|
|
20782
|
+
}
|
|
20783
|
+
}
|
|
20784
|
+
});
|
|
20785
|
+
|
|
20786
|
+
// ../../packages/analyzers/dist/typescript/env-typo-detector.js
|
|
20787
|
+
var require_env_typo_detector = __commonJS({
|
|
20788
|
+
"../../packages/analyzers/dist/typescript/env-typo-detector.js"(exports2) {
|
|
20789
|
+
"use strict";
|
|
20790
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
20791
|
+
if (k2 === void 0) k2 = k;
|
|
20792
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20793
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20794
|
+
desc = { enumerable: true, get: function() {
|
|
20795
|
+
return m[k];
|
|
20796
|
+
} };
|
|
20797
|
+
}
|
|
20798
|
+
Object.defineProperty(o, k2, desc);
|
|
20799
|
+
}) : (function(o, m, k, k2) {
|
|
20800
|
+
if (k2 === void 0) k2 = k;
|
|
20801
|
+
o[k2] = m[k];
|
|
20802
|
+
}));
|
|
20803
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
20804
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20805
|
+
}) : function(o, v) {
|
|
20806
|
+
o["default"] = v;
|
|
20807
|
+
});
|
|
20808
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
20809
|
+
var ownKeys = function(o) {
|
|
20810
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
20811
|
+
var ar = [];
|
|
20812
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
20813
|
+
return ar;
|
|
20814
|
+
};
|
|
20815
|
+
return ownKeys(o);
|
|
20816
|
+
};
|
|
20817
|
+
return function(mod) {
|
|
20818
|
+
if (mod && mod.__esModule) return mod;
|
|
20819
|
+
var result = {};
|
|
20820
|
+
if (mod != null) {
|
|
20821
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
20822
|
+
}
|
|
20823
|
+
__setModuleDefault(result, mod);
|
|
20824
|
+
return result;
|
|
20825
|
+
};
|
|
20826
|
+
})();
|
|
20827
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20828
|
+
exports2.detectProcessEnvTypo = detectProcessEnvTypo;
|
|
20829
|
+
var fs9 = __importStar(require("fs"));
|
|
20830
|
+
var path9 = __importStar(require("path"));
|
|
20831
|
+
var shared_1 = require_dist();
|
|
20832
|
+
var ts_morph_1 = require("ts-morph");
|
|
20833
|
+
var parsed_files_1 = require_parsed_files();
|
|
20834
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.fixture\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
20835
|
+
var PLATFORM_PROVIDED_ENV_VARS = /* @__PURE__ */ new Set([
|
|
20836
|
+
// Node / shell runtime
|
|
20837
|
+
"NODE_ENV",
|
|
20838
|
+
"PORT",
|
|
20839
|
+
"PWD",
|
|
20840
|
+
"HOME",
|
|
20841
|
+
"USER",
|
|
20842
|
+
"PATH",
|
|
20843
|
+
"SHELL",
|
|
20844
|
+
"TERM",
|
|
20845
|
+
"TZ",
|
|
20846
|
+
"LANG",
|
|
20847
|
+
"LC_ALL",
|
|
20848
|
+
"LC_TIME",
|
|
20849
|
+
"DEBUG",
|
|
20850
|
+
"NODE_OPTIONS",
|
|
20851
|
+
"CI",
|
|
20852
|
+
// GitHub Actions — runner-injected, NOT app config
|
|
20853
|
+
"GITHUB_ACTIONS",
|
|
20854
|
+
"GITHUB_TOKEN",
|
|
20855
|
+
"GITHUB_SHA",
|
|
20856
|
+
"GITHUB_REPOSITORY",
|
|
20857
|
+
"GITHUB_REPOSITORY_OWNER",
|
|
20858
|
+
"GITHUB_REF",
|
|
20859
|
+
"GITHUB_REF_NAME",
|
|
20860
|
+
"GITHUB_REF_TYPE",
|
|
20861
|
+
"GITHUB_HEAD_REF",
|
|
20862
|
+
"GITHUB_BASE_REF",
|
|
20863
|
+
"GITHUB_RUN_ID",
|
|
20864
|
+
"GITHUB_RUN_NUMBER",
|
|
20865
|
+
"GITHUB_RUN_ATTEMPT",
|
|
20866
|
+
"GITHUB_WORKFLOW",
|
|
20867
|
+
"GITHUB_EVENT_NAME",
|
|
20868
|
+
"GITHUB_EVENT_PATH",
|
|
20869
|
+
"GITHUB_WORKSPACE",
|
|
20870
|
+
"GITHUB_ACTOR",
|
|
20871
|
+
"GITHUB_ACTOR_ID",
|
|
20872
|
+
"GITHUB_API_URL",
|
|
20873
|
+
"GITHUB_SERVER_URL",
|
|
20874
|
+
"GITHUB_GRAPHQL_URL",
|
|
20875
|
+
"GITHUB_JOB",
|
|
20876
|
+
"GITHUB_ACTION",
|
|
20877
|
+
"GITHUB_ACTION_PATH",
|
|
20878
|
+
"GITHUB_ACTION_REPOSITORY",
|
|
20879
|
+
"GITHUB_RETENTION_DAYS",
|
|
20880
|
+
// GitLab CI — runner-injected
|
|
20881
|
+
"GITLAB_CI",
|
|
20882
|
+
"GITLAB_USER_ID",
|
|
20883
|
+
"GITLAB_USER_LOGIN",
|
|
20884
|
+
"CI_JOB_TOKEN",
|
|
20885
|
+
"CI_COMMIT_SHA",
|
|
20886
|
+
"CI_COMMIT_REF_NAME",
|
|
20887
|
+
"CI_COMMIT_BRANCH",
|
|
20888
|
+
"CI_COMMIT_TAG",
|
|
20889
|
+
"CI_PROJECT_ID",
|
|
20890
|
+
"CI_PROJECT_PATH",
|
|
20891
|
+
"CI_PROJECT_NAME",
|
|
20892
|
+
"CI_PIPELINE_ID",
|
|
20893
|
+
"CI_PIPELINE_IID",
|
|
20894
|
+
"CI_PIPELINE_SOURCE",
|
|
20895
|
+
"CI_JOB_ID",
|
|
20896
|
+
"CI_MERGE_REQUEST_IID",
|
|
20897
|
+
"CI_MERGE_REQUEST_PROJECT_PATH",
|
|
20898
|
+
"CI_API_V4_URL",
|
|
20899
|
+
"CI_SERVER_URL",
|
|
20900
|
+
// Railway, Vercel, Netlify — platform-injected
|
|
20901
|
+
"RAILWAY_ENVIRONMENT",
|
|
20902
|
+
"RAILWAY_PROJECT_ID",
|
|
20903
|
+
"RAILWAY_PROJECT_NAME",
|
|
20904
|
+
"RAILWAY_SERVICE_ID",
|
|
20905
|
+
"RAILWAY_SERVICE_NAME",
|
|
20906
|
+
"RAILWAY_REPLICA_ID",
|
|
20907
|
+
"RAILWAY_STATIC_URL",
|
|
20908
|
+
"RAILWAY_PUBLIC_DOMAIN",
|
|
20909
|
+
"RAILWAY_PRIVATE_DOMAIN",
|
|
20910
|
+
"VERCEL",
|
|
20911
|
+
"VERCEL_ENV",
|
|
20912
|
+
"VERCEL_URL",
|
|
20913
|
+
"VERCEL_BRANCH_URL",
|
|
20914
|
+
"VERCEL_REGION",
|
|
20915
|
+
"VERCEL_GIT_COMMIT_SHA",
|
|
20916
|
+
"VERCEL_GIT_COMMIT_REF",
|
|
20917
|
+
"VERCEL_GIT_PROVIDER",
|
|
20918
|
+
"VERCEL_GIT_REPO_SLUG",
|
|
20919
|
+
"VERCEL_GIT_REPO_OWNER",
|
|
20920
|
+
"NETLIFY",
|
|
20921
|
+
"NETLIFY_BUILD_BASE",
|
|
20922
|
+
"NETLIFY_LOCAL",
|
|
20923
|
+
"NETLIFY_DEV",
|
|
20924
|
+
"CONTEXT",
|
|
20925
|
+
"DEPLOY_URL",
|
|
20926
|
+
"DEPLOY_PRIME_URL",
|
|
20927
|
+
"BUILD_ID",
|
|
20928
|
+
// Turbo internals
|
|
20929
|
+
"TURBO_TEAM",
|
|
20930
|
+
"TURBO_TOKEN",
|
|
20931
|
+
"TURBO_REMOTE_CACHE_SIGNATURE_KEY"
|
|
20932
|
+
]);
|
|
20933
|
+
function isPlatformProvidedEnvVar(name) {
|
|
20934
|
+
if (PLATFORM_PROVIDED_ENV_VARS.has(name))
|
|
20935
|
+
return true;
|
|
20936
|
+
if (/^npm_(config_|package_|lifecycle_|execpath)/i.test(name))
|
|
20937
|
+
return true;
|
|
20938
|
+
return false;
|
|
20939
|
+
}
|
|
20940
|
+
async function detectProcessEnvTypo(input, projectRoot, policy, parsed) {
|
|
20941
|
+
if (policy.maintainability?.check_env_drift !== true)
|
|
20942
|
+
return [];
|
|
20943
|
+
const declared = await readDeclaredEnvVars(projectRoot);
|
|
20944
|
+
if (!declared)
|
|
20945
|
+
return [];
|
|
20946
|
+
const violations = [];
|
|
20947
|
+
for (const { path: path10, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
20948
|
+
if (EXCLUDED_PATH_REGEX.test(path10))
|
|
20949
|
+
continue;
|
|
20950
|
+
scanFile(sourceFile, path10, declared, policy, violations);
|
|
20951
|
+
}
|
|
20952
|
+
return violations;
|
|
20953
|
+
}
|
|
20954
|
+
async function readDeclaredEnvVars(projectRoot) {
|
|
20955
|
+
const envPath = path9.join(projectRoot, ".env.example");
|
|
20956
|
+
let content;
|
|
20957
|
+
try {
|
|
20958
|
+
content = await fs9.promises.readFile(envPath, "utf-8");
|
|
20959
|
+
} catch {
|
|
20960
|
+
return void 0;
|
|
20961
|
+
}
|
|
20962
|
+
const declared = /* @__PURE__ */ new Set();
|
|
20963
|
+
for (const line of content.split("\n")) {
|
|
20964
|
+
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/);
|
|
20965
|
+
if (match)
|
|
20966
|
+
declared.add(match[1]);
|
|
20967
|
+
}
|
|
20968
|
+
return declared;
|
|
20969
|
+
}
|
|
20970
|
+
function scanFile(sourceFile, filePath, declared, policy, violations) {
|
|
20971
|
+
void policy;
|
|
20972
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20973
|
+
sourceFile.forEachDescendant((node) => {
|
|
20974
|
+
const name = referencedEnvVar(node);
|
|
20975
|
+
if (!name)
|
|
20976
|
+
return;
|
|
20977
|
+
if (declared.has(name) || isPlatformProvidedEnvVar(name))
|
|
20978
|
+
return;
|
|
20979
|
+
if (seen.has(name))
|
|
20980
|
+
return;
|
|
20981
|
+
seen.add(name);
|
|
20982
|
+
violations.push({
|
|
20983
|
+
category: "maintainability",
|
|
20984
|
+
type: shared_1.MAINTAINABILITY_RULES.PROCESS_ENV_TYPO,
|
|
20985
|
+
ruleId: shared_1.MAINTAINABILITY_RULES.PROCESS_ENV_TYPO,
|
|
20986
|
+
severity: "warning",
|
|
20987
|
+
source: "deterministic",
|
|
20988
|
+
confidence: "medium",
|
|
20989
|
+
file: filePath,
|
|
20990
|
+
line: node.getStartLineNumber(),
|
|
20991
|
+
message: `Env var ${name} is referenced in code but not declared in .env.example \u2014 it is either a typo or a missing entry.`,
|
|
20992
|
+
suggestion: `Add ${name}= to .env.example, or correct the name in code to match a declared variable.`,
|
|
20993
|
+
debtPoints: 2,
|
|
20994
|
+
gateAction: "warn"
|
|
20995
|
+
});
|
|
20996
|
+
});
|
|
20997
|
+
}
|
|
20998
|
+
function referencedEnvVar(node) {
|
|
20999
|
+
if (ts_morph_1.Node.isPropertyAccessExpression(node)) {
|
|
21000
|
+
if (node.getExpression().getText() === "process.env") {
|
|
21001
|
+
return node.getName();
|
|
21002
|
+
}
|
|
21003
|
+
}
|
|
21004
|
+
if (ts_morph_1.Node.isElementAccessExpression(node)) {
|
|
21005
|
+
if (node.getExpression().getText() === "process.env") {
|
|
21006
|
+
const arg = node.getArgumentExpression();
|
|
21007
|
+
if (arg && (ts_morph_1.Node.isStringLiteral(arg) || ts_morph_1.Node.isNoSubstitutionTemplateLiteral(arg))) {
|
|
21008
|
+
return arg.getLiteralValue();
|
|
21009
|
+
}
|
|
21010
|
+
}
|
|
21011
|
+
}
|
|
21012
|
+
return void 0;
|
|
21013
|
+
}
|
|
21014
|
+
}
|
|
21015
|
+
});
|
|
21016
|
+
|
|
21017
|
+
// ../../packages/analyzers/dist/typescript/maintainability-detector.js
|
|
21018
|
+
var require_maintainability_detector = __commonJS({
|
|
21019
|
+
"../../packages/analyzers/dist/typescript/maintainability-detector.js"(exports2) {
|
|
21020
|
+
"use strict";
|
|
21021
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
21022
|
+
exports2.detectMaintainabilityIssues = detectMaintainabilityIssues;
|
|
21023
|
+
var shared_1 = require_dist();
|
|
21024
|
+
var ts_morph_1 = require("ts-morph");
|
|
21025
|
+
var parsed_files_1 = require_parsed_files();
|
|
21026
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
21027
|
+
var DEFAULT_THRESHOLDS = {
|
|
21028
|
+
functionLength: shared_1.DEFAULT_FUNCTION_LENGTH_THRESHOLD,
|
|
21029
|
+
parameterCount: shared_1.DEFAULT_PARAMETER_COUNT_THRESHOLD,
|
|
21030
|
+
nestingDepth: shared_1.DEFAULT_NESTING_DEPTH_THRESHOLD
|
|
21031
|
+
};
|
|
21032
|
+
async function detectMaintainabilityIssues(input, policy, thresholds = {}, parsed) {
|
|
21033
|
+
const limits = { ...DEFAULT_THRESHOLDS, ...thresholds };
|
|
21034
|
+
const violations = [];
|
|
21035
|
+
for (const { path: path9, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
21036
|
+
if (EXCLUDED_PATH_REGEX.test(path9))
|
|
21037
|
+
continue;
|
|
21038
|
+
scanFile(sourceFile, path9, limits, policy, violations);
|
|
21039
|
+
}
|
|
21040
|
+
return violations;
|
|
21041
|
+
}
|
|
21042
|
+
function scanFile(sourceFile, filePath, limits, policy, violations) {
|
|
21043
|
+
void policy;
|
|
21044
|
+
sourceFile.forEachDescendant((node) => {
|
|
21045
|
+
if (!isFunctionLike(node))
|
|
21046
|
+
return;
|
|
21047
|
+
const body = node.getBody();
|
|
21048
|
+
if (body) {
|
|
21049
|
+
const lineSpan = body.getEndLineNumber() - body.getStartLineNumber();
|
|
21050
|
+
if (lineSpan > limits.functionLength) {
|
|
21051
|
+
const name = getFunctionName(node);
|
|
21052
|
+
violations.push({
|
|
21053
|
+
category: "maintainability",
|
|
21054
|
+
type: shared_1.MAINTAINABILITY_RULES.FUNCTION_TOO_LONG,
|
|
21055
|
+
ruleId: shared_1.MAINTAINABILITY_RULES.FUNCTION_TOO_LONG,
|
|
21056
|
+
severity: "warning",
|
|
21057
|
+
source: "deterministic",
|
|
21058
|
+
confidence: "high",
|
|
21059
|
+
file: filePath,
|
|
21060
|
+
line: node.getStartLineNumber(),
|
|
21061
|
+
function: name,
|
|
21062
|
+
message: `Function '${name}' is ${lineSpan} lines long (threshold: ${limits.functionLength}) \u2014 long functions are hard to read, test, and reuse.`,
|
|
21063
|
+
suggestion: "Extract cohesive blocks into well-named helper functions.",
|
|
21064
|
+
debtPoints: 2,
|
|
21065
|
+
gateAction: "warn"
|
|
21066
|
+
});
|
|
21067
|
+
}
|
|
21068
|
+
const depth = maxNestingDepth(body);
|
|
21069
|
+
if (depth > limits.nestingDepth) {
|
|
21070
|
+
const name = getFunctionName(node);
|
|
21071
|
+
violations.push({
|
|
21072
|
+
category: "maintainability",
|
|
21073
|
+
type: shared_1.MAINTAINABILITY_RULES.DEEP_NESTING,
|
|
21074
|
+
ruleId: shared_1.MAINTAINABILITY_RULES.DEEP_NESTING,
|
|
21075
|
+
severity: "warning",
|
|
21076
|
+
source: "deterministic",
|
|
21077
|
+
confidence: "high",
|
|
21078
|
+
file: filePath,
|
|
21079
|
+
line: node.getStartLineNumber(),
|
|
21080
|
+
function: name,
|
|
21081
|
+
message: `Function '${name}' nests blocks ${depth} levels deep (threshold: ${limits.nestingDepth}) \u2014 deep nesting hides the control flow.`,
|
|
21082
|
+
suggestion: "Flatten with early returns / guard clauses, or extract inner blocks into helper functions.",
|
|
21083
|
+
debtPoints: 2,
|
|
21084
|
+
gateAction: "warn"
|
|
21085
|
+
});
|
|
21086
|
+
}
|
|
21087
|
+
}
|
|
21088
|
+
const paramCount = node.getParameters().length;
|
|
21089
|
+
if (paramCount > limits.parameterCount) {
|
|
21090
|
+
const name = getFunctionName(node);
|
|
21091
|
+
violations.push({
|
|
21092
|
+
category: "maintainability",
|
|
21093
|
+
type: shared_1.MAINTAINABILITY_RULES.TOO_MANY_PARAMETERS,
|
|
21094
|
+
ruleId: shared_1.MAINTAINABILITY_RULES.TOO_MANY_PARAMETERS,
|
|
21095
|
+
severity: "warning",
|
|
21096
|
+
source: "deterministic",
|
|
21097
|
+
confidence: "high",
|
|
21098
|
+
file: filePath,
|
|
21099
|
+
line: node.getStartLineNumber(),
|
|
21100
|
+
function: name,
|
|
21101
|
+
message: `Function '${name}' has ${paramCount} parameters (threshold: ${limits.parameterCount}) \u2014 long parameter lists are easy to mis-order and hard to call.`,
|
|
21102
|
+
suggestion: "Group related parameters into a single options object.",
|
|
21103
|
+
debtPoints: 2,
|
|
21104
|
+
gateAction: "warn"
|
|
21105
|
+
});
|
|
21106
|
+
}
|
|
21107
|
+
});
|
|
21108
|
+
}
|
|
21109
|
+
function isFunctionLike(node) {
|
|
21110
|
+
return ts_morph_1.Node.isFunctionDeclaration(node) || ts_morph_1.Node.isFunctionExpression(node) || ts_morph_1.Node.isArrowFunction(node) || ts_morph_1.Node.isMethodDeclaration(node) || ts_morph_1.Node.isConstructorDeclaration(node) || ts_morph_1.Node.isGetAccessorDeclaration(node) || ts_morph_1.Node.isSetAccessorDeclaration(node);
|
|
21111
|
+
}
|
|
21112
|
+
function getFunctionName(node) {
|
|
21113
|
+
if (ts_morph_1.Node.isConstructorDeclaration(node))
|
|
21114
|
+
return "constructor";
|
|
21115
|
+
if (ts_morph_1.Node.isFunctionDeclaration(node) || ts_morph_1.Node.isMethodDeclaration(node) || ts_morph_1.Node.isGetAccessorDeclaration(node) || ts_morph_1.Node.isSetAccessorDeclaration(node)) {
|
|
21116
|
+
return node.getName() ?? "<anonymous>";
|
|
21117
|
+
}
|
|
21118
|
+
const parent = node.getParent();
|
|
21119
|
+
if (ts_morph_1.Node.isVariableDeclaration(parent) || ts_morph_1.Node.isPropertyAssignment(parent)) {
|
|
21120
|
+
return parent.getName();
|
|
21121
|
+
}
|
|
21122
|
+
if (ts_morph_1.Node.isFunctionExpression(node)) {
|
|
21123
|
+
const own = node.getName();
|
|
21124
|
+
if (own)
|
|
21125
|
+
return own;
|
|
21126
|
+
}
|
|
21127
|
+
return "<anonymous>";
|
|
21128
|
+
}
|
|
21129
|
+
function maxNestingDepth(body) {
|
|
21130
|
+
let max = 0;
|
|
21131
|
+
function recurse(node, depth) {
|
|
21132
|
+
node.forEachChild((child) => {
|
|
21133
|
+
if (isFunctionLike(child))
|
|
21134
|
+
return;
|
|
21135
|
+
let childDepth = depth;
|
|
21136
|
+
if (isNestingNode(child)) {
|
|
21137
|
+
if (ts_morph_1.Node.isIfStatement(child) && isElseIf(child)) {
|
|
21138
|
+
childDepth = depth;
|
|
21139
|
+
} else {
|
|
21140
|
+
childDepth = depth + 1;
|
|
21141
|
+
if (childDepth > max)
|
|
21142
|
+
max = childDepth;
|
|
21143
|
+
}
|
|
21144
|
+
}
|
|
21145
|
+
recurse(child, childDepth);
|
|
21146
|
+
});
|
|
21147
|
+
}
|
|
21148
|
+
recurse(body, 0);
|
|
21149
|
+
return max;
|
|
21150
|
+
}
|
|
21151
|
+
function isNestingNode(node) {
|
|
21152
|
+
return ts_morph_1.Node.isIfStatement(node) || ts_morph_1.Node.isForStatement(node) || ts_morph_1.Node.isForInStatement(node) || ts_morph_1.Node.isForOfStatement(node) || ts_morph_1.Node.isWhileStatement(node) || ts_morph_1.Node.isDoStatement(node) || ts_morph_1.Node.isTryStatement(node) || ts_morph_1.Node.isSwitchStatement(node) || ts_morph_1.Node.isCaseClause(node) || ts_morph_1.Node.isDefaultClause(node);
|
|
21153
|
+
}
|
|
21154
|
+
function isElseIf(node) {
|
|
21155
|
+
const parent = node.getParent();
|
|
21156
|
+
return parent !== void 0 && ts_morph_1.Node.isIfStatement(parent) && parent.getElseStatement() === node;
|
|
21157
|
+
}
|
|
21158
|
+
}
|
|
21159
|
+
});
|
|
21160
|
+
|
|
21161
|
+
// ../../packages/analyzers/dist/typescript/console-in-production-detector.js
|
|
21162
|
+
var require_console_in_production_detector = __commonJS({
|
|
21163
|
+
"../../packages/analyzers/dist/typescript/console-in-production-detector.js"(exports2) {
|
|
21164
|
+
"use strict";
|
|
21165
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
21166
|
+
exports2.detectConsoleInProduction = detectConsoleInProduction;
|
|
21167
|
+
var shared_1 = require_dist();
|
|
21168
|
+
var ts_morph_1 = require("ts-morph");
|
|
21169
|
+
var parsed_files_1 = require_parsed_files();
|
|
21170
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
21171
|
+
var ALLOW_COMMENT = "radar:allow-console";
|
|
21172
|
+
async function detectConsoleInProduction(input, policy, parsed) {
|
|
21173
|
+
const violations = [];
|
|
21174
|
+
for (const { path: path9, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
21175
|
+
if (isExcludedConsoleFile(path9))
|
|
21176
|
+
continue;
|
|
21177
|
+
if (hasAllowConsoleComment(sourceFile.getFullText()))
|
|
21178
|
+
continue;
|
|
21179
|
+
scanFile(sourceFile, path9, policy, violations);
|
|
21180
|
+
}
|
|
21181
|
+
return violations;
|
|
21182
|
+
}
|
|
21183
|
+
function isExcludedConsoleFile(filePath) {
|
|
21184
|
+
if (EXCLUDED_PATH_REGEX.test(filePath))
|
|
21185
|
+
return true;
|
|
21186
|
+
if (/(^|\/)apps\/cli\/src\//.test(filePath))
|
|
21187
|
+
return true;
|
|
21188
|
+
if (/(^|\/)(scripts|build)\//.test(filePath))
|
|
21189
|
+
return true;
|
|
21190
|
+
const base = filePath.split("/").pop() ?? "";
|
|
21191
|
+
if (base === "index.ts" || base === "index.js")
|
|
21192
|
+
return true;
|
|
21193
|
+
return false;
|
|
21194
|
+
}
|
|
21195
|
+
function hasAllowConsoleComment(content) {
|
|
21196
|
+
return content.split("\n", 5).join("\n").includes(ALLOW_COMMENT);
|
|
21197
|
+
}
|
|
21198
|
+
function scanFile(sourceFile, filePath, policy, violations) {
|
|
21199
|
+
void policy;
|
|
21200
|
+
sourceFile.forEachDescendant((node) => {
|
|
21201
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
21202
|
+
return;
|
|
21203
|
+
const expr = node.getExpression();
|
|
21204
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
21205
|
+
return;
|
|
21206
|
+
if (expr.getExpression().getText() !== "console")
|
|
21207
|
+
return;
|
|
21208
|
+
const method = expr.getName();
|
|
21209
|
+
violations.push({
|
|
21210
|
+
category: "maintainability",
|
|
21211
|
+
type: shared_1.MAINTAINABILITY_RULES.CONSOLE_LOG_IN_PRODUCTION,
|
|
21212
|
+
ruleId: shared_1.MAINTAINABILITY_RULES.CONSOLE_LOG_IN_PRODUCTION,
|
|
21213
|
+
severity: "info",
|
|
21214
|
+
source: "deterministic",
|
|
21215
|
+
confidence: "high",
|
|
21216
|
+
file: filePath,
|
|
21217
|
+
line: node.getStartLineNumber(),
|
|
21218
|
+
function: getEnclosingFunctionName(node),
|
|
21219
|
+
message: `console.${method}() in production source \u2014 console output is unstructured and bypasses log levels and transports.`,
|
|
21220
|
+
suggestion: "Use the project's structured logger, or remove the call if it was a debugging leftover. Add `// radar:allow-console` at the top of the file to opt out.",
|
|
21221
|
+
debtPoints: 1,
|
|
21222
|
+
gateAction: "none"
|
|
21223
|
+
});
|
|
21224
|
+
});
|
|
21225
|
+
}
|
|
21226
|
+
function getEnclosingFunctionName(node) {
|
|
21227
|
+
const fn = node.getFirstAncestor((a) => ts_morph_1.Node.isFunctionDeclaration(a) || ts_morph_1.Node.isMethodDeclaration(a));
|
|
21228
|
+
if (fn && (ts_morph_1.Node.isFunctionDeclaration(fn) || ts_morph_1.Node.isMethodDeclaration(fn))) {
|
|
21229
|
+
return fn.getName() ?? void 0;
|
|
21230
|
+
}
|
|
21231
|
+
return void 0;
|
|
21232
|
+
}
|
|
21233
|
+
}
|
|
21234
|
+
});
|
|
21235
|
+
|
|
21236
|
+
// ../../packages/analyzers/dist/typescript/todo-comment-detector.js
|
|
21237
|
+
var require_todo_comment_detector = __commonJS({
|
|
21238
|
+
"../../packages/analyzers/dist/typescript/todo-comment-detector.js"(exports2) {
|
|
21239
|
+
"use strict";
|
|
21240
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
21241
|
+
exports2.detectStrayTodoComments = detectStrayTodoComments;
|
|
21242
|
+
var shared_1 = require_dist();
|
|
21243
|
+
var parsed_files_1 = require_parsed_files();
|
|
21244
|
+
var EXCLUDED_PATH_REGEX = /(\.spec\.|\.test\.|\.d\.ts$)|(^|\/)(__tests__|__mocks__|test|tests|e2e|mocks)(\/|$)/i;
|
|
21245
|
+
var MARKER_REGEX = /\b(TODO|FIXME|HACK|XXX|TBD)\b\s*(\([^)]*\))?/gi;
|
|
21246
|
+
var TRACKER_REGEX = /#\d+|[A-Z][A-Z0-9]+-\d+|https?:\/\//;
|
|
21247
|
+
var LICENSE_REGEX = /SPDX-License-Identifier:|\bCopyright\b|\bLicen[sc]e\b/i;
|
|
21248
|
+
async function detectStrayTodoComments(input, policy, parsed) {
|
|
21249
|
+
const violations = [];
|
|
21250
|
+
for (const { path: path9, sourceFile } of parsed ?? (0, parsed_files_1.parseChangedFiles)(input)) {
|
|
21251
|
+
if (EXCLUDED_PATH_REGEX.test(path9))
|
|
21252
|
+
continue;
|
|
21253
|
+
scanFile(sourceFile, path9, policy, violations);
|
|
21254
|
+
}
|
|
21255
|
+
return violations;
|
|
21256
|
+
}
|
|
21257
|
+
function scanFile(sourceFile, filePath, policy, violations) {
|
|
21258
|
+
void policy;
|
|
21259
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21260
|
+
const consider = (range) => {
|
|
21261
|
+
const pos = range.getPos();
|
|
21262
|
+
if (seen.has(pos))
|
|
21263
|
+
return;
|
|
21264
|
+
seen.add(pos);
|
|
21265
|
+
const keyword = strayMarkerKeyword(range.getText());
|
|
21266
|
+
if (!keyword)
|
|
21267
|
+
return;
|
|
21268
|
+
const { line } = sourceFile.getLineAndColumnAtPos(pos);
|
|
21269
|
+
violations.push({
|
|
21270
|
+
category: "maintainability",
|
|
21271
|
+
type: shared_1.MAINTAINABILITY_RULES.STRAY_TODO_COMMENT,
|
|
21272
|
+
ruleId: shared_1.MAINTAINABILITY_RULES.STRAY_TODO_COMMENT,
|
|
21273
|
+
severity: "info",
|
|
21274
|
+
source: "deterministic",
|
|
21275
|
+
confidence: "high",
|
|
21276
|
+
file: filePath,
|
|
21277
|
+
line,
|
|
21278
|
+
message: `Stray ${keyword} comment in committed code \u2014 unresolved markers accumulate as silent, untracked debt.`,
|
|
21279
|
+
suggestion: `Resolve the ${keyword}, or link it to a tracked issue, e.g. ${keyword}(#123): ....`,
|
|
21280
|
+
debtPoints: 1,
|
|
21281
|
+
gateAction: "none"
|
|
21282
|
+
});
|
|
21283
|
+
};
|
|
21284
|
+
sourceFile.forEachDescendant((node) => {
|
|
21285
|
+
for (const r of node.getLeadingCommentRanges())
|
|
21286
|
+
consider(r);
|
|
21287
|
+
for (const r of node.getTrailingCommentRanges())
|
|
21288
|
+
consider(r);
|
|
21289
|
+
});
|
|
21290
|
+
}
|
|
21291
|
+
function strayMarkerKeyword(commentText) {
|
|
21292
|
+
if (LICENSE_REGEX.test(commentText))
|
|
21293
|
+
return void 0;
|
|
21294
|
+
MARKER_REGEX.lastIndex = 0;
|
|
21295
|
+
let match;
|
|
21296
|
+
while ((match = MARKER_REGEX.exec(commentText)) !== null) {
|
|
21297
|
+
const reference = match[2] ?? "";
|
|
21298
|
+
if (!TRACKER_REGEX.test(reference)) {
|
|
21299
|
+
return match[1].toUpperCase();
|
|
21300
|
+
}
|
|
21301
|
+
}
|
|
21302
|
+
return void 0;
|
|
21303
|
+
}
|
|
21304
|
+
}
|
|
21305
|
+
});
|
|
21306
|
+
|
|
20375
21307
|
// ../../packages/analyzers/dist/typescript/orchestrator.js
|
|
20376
21308
|
var require_orchestrator = __commonJS({
|
|
20377
21309
|
"../../packages/analyzers/dist/typescript/orchestrator.js"(exports2) {
|
|
@@ -20433,6 +21365,14 @@ var require_orchestrator = __commonJS({
|
|
|
20433
21365
|
var eval_detector_1 = require_eval_detector();
|
|
20434
21366
|
var math_random_detector_1 = require_math_random_detector();
|
|
20435
21367
|
var tls_detector_1 = require_tls_detector();
|
|
21368
|
+
var cors_detector_1 = require_cors_detector();
|
|
21369
|
+
var jwt_detector_1 = require_jwt_detector();
|
|
21370
|
+
var jsx_xss_detector_1 = require_jsx_xss_detector();
|
|
21371
|
+
var env_typo_detector_1 = require_env_typo_detector();
|
|
21372
|
+
var maintainability_detector_1 = require_maintainability_detector();
|
|
21373
|
+
var console_in_production_detector_1 = require_console_in_production_detector();
|
|
21374
|
+
var todo_comment_detector_1 = require_todo_comment_detector();
|
|
21375
|
+
var parsed_files_1 = require_parsed_files();
|
|
20436
21376
|
var EXCLUDED_FILE_PATTERNS = [
|
|
20437
21377
|
/\.spec\.(ts|tsx|js|jsx)$/,
|
|
20438
21378
|
/\.test\.(ts|tsx|js|jsx)$/,
|
|
@@ -20461,7 +21401,8 @@ var require_orchestrator = __commonJS({
|
|
|
20461
21401
|
changedFiles: input.changedFiles.filter((f) => !isExcludedFile(f.path))
|
|
20462
21402
|
};
|
|
20463
21403
|
const projectRoot = input.projectRoot ?? deriveProjectRoot(input);
|
|
20464
|
-
const
|
|
21404
|
+
const parsedFiles = (0, parsed_files_1.parseChangedFiles)(filteredInput);
|
|
21405
|
+
const [importGraph, complexityDeltas, runtimeResult, perfViolations, reliabilityViolations, duplicationResult, missingTestsResult, deadCodeResult, coverageDeltaResult, secretViolations, evalViolations, mathRandomViolations, tlsViolations, corsViolations, jwtViolations, jsxXssViolations, envTypoViolations, maintainabilityViolations, consoleViolations, todoViolations] = await Promise.all([
|
|
20465
21406
|
(0, import_graph_1.buildImportGraph)(filteredInput, policy),
|
|
20466
21407
|
(0, complexity_calculator_1.calculateComplexity)(filteredInput),
|
|
20467
21408
|
(0, runtime_risk_detector_1.detectRuntimeRisks)(filteredInput, policy),
|
|
@@ -20474,7 +21415,14 @@ var require_orchestrator = __commonJS({
|
|
|
20474
21415
|
(0, secret_scanner_1.detectHardcodedSecrets)(filteredInput, policy),
|
|
20475
21416
|
(0, eval_detector_1.detectEvalUsage)(filteredInput, policy),
|
|
20476
21417
|
(0, math_random_detector_1.detectMathRandomForSecurity)(filteredInput, policy),
|
|
20477
|
-
(0, tls_detector_1.detectTlsValidationDisabled)(filteredInput, policy)
|
|
21418
|
+
(0, tls_detector_1.detectTlsValidationDisabled)(filteredInput, policy),
|
|
21419
|
+
(0, cors_detector_1.detectCorsWildcardCredentials)(filteredInput, policy, parsedFiles),
|
|
21420
|
+
(0, jwt_detector_1.detectJwtInsecureOptions)(filteredInput, policy, parsedFiles),
|
|
21421
|
+
(0, jsx_xss_detector_1.detectDangerouslySetInnerHtml)(filteredInput, policy, parsedFiles),
|
|
21422
|
+
projectRoot ? (0, env_typo_detector_1.detectProcessEnvTypo)(filteredInput, projectRoot, policy, parsedFiles) : Promise.resolve([]),
|
|
21423
|
+
(0, maintainability_detector_1.detectMaintainabilityIssues)(filteredInput, policy, {}, parsedFiles),
|
|
21424
|
+
(0, console_in_production_detector_1.detectConsoleInProduction)(filteredInput, policy, parsedFiles),
|
|
21425
|
+
(0, todo_comment_detector_1.detectStrayTodoComments)(filteredInput, policy, parsedFiles)
|
|
20478
21426
|
]);
|
|
20479
21427
|
const runtimeViolations = runtimeResult.violations;
|
|
20480
21428
|
const unflaggedPatterns = runtimeResult.unflaggedPatterns;
|
|
@@ -20493,6 +21441,13 @@ var require_orchestrator = __commonJS({
|
|
|
20493
21441
|
...evalViolations,
|
|
20494
21442
|
...mathRandomViolations,
|
|
20495
21443
|
...tlsViolations,
|
|
21444
|
+
...corsViolations,
|
|
21445
|
+
...jwtViolations,
|
|
21446
|
+
...jsxXssViolations,
|
|
21447
|
+
...envTypoViolations,
|
|
21448
|
+
...maintainabilityViolations,
|
|
21449
|
+
...consoleViolations,
|
|
21450
|
+
...todoViolations,
|
|
20496
21451
|
...boundaryViolations,
|
|
20497
21452
|
...circularViolations,
|
|
20498
21453
|
...runtimeViolations,
|
|
@@ -20837,7 +21792,7 @@ var require_dist3 = __commonJS({
|
|
|
20837
21792
|
"../../packages/analyzers/dist/index.js"(exports2) {
|
|
20838
21793
|
"use strict";
|
|
20839
21794
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20840
|
-
exports2.runFullAnalysis = exports2.detectTlsValidationDisabled = exports2.detectMathRandomForSecurity = exports2.detectEvalUsage = exports2.detectHardcodedSecrets = exports2.detectCoverageDelta = exports2.detectDeadCode = exports2.analyzeCrossFileWithAI = exports2.buildReverseImportGraph = exports2.analyzeCrossFile = exports2.detectMissingTests = exports2.detectDuplication = exports2.detectReliabilityIssues = exports2.detectPerformanceRisks = exports2.detectRuntimeRisks = exports2.calculateComplexity = exports2.detectCircularDeps = exports2.checkBoundaries = exports2.buildImportGraph = void 0;
|
|
21795
|
+
exports2.runFullAnalysis = exports2.detectStrayTodoComments = exports2.detectConsoleInProduction = exports2.detectMaintainabilityIssues = exports2.detectProcessEnvTypo = exports2.detectDangerouslySetInnerHtml = exports2.detectJwtInsecureOptions = exports2.detectCorsWildcardCredentials = exports2.detectTlsValidationDisabled = exports2.detectMathRandomForSecurity = exports2.detectEvalUsage = exports2.detectHardcodedSecrets = exports2.detectCoverageDelta = exports2.detectDeadCode = exports2.analyzeCrossFileWithAI = exports2.buildReverseImportGraph = exports2.analyzeCrossFile = exports2.detectMissingTests = exports2.detectDuplication = exports2.detectReliabilityIssues = exports2.detectPerformanceRisks = exports2.detectRuntimeRisks = exports2.calculateComplexity = exports2.detectCircularDeps = exports2.checkBoundaries = exports2.buildImportGraph = void 0;
|
|
20841
21796
|
var import_graph_1 = require_import_graph();
|
|
20842
21797
|
Object.defineProperty(exports2, "buildImportGraph", { enumerable: true, get: function() {
|
|
20843
21798
|
return import_graph_1.buildImportGraph;
|
|
@@ -20909,6 +21864,34 @@ var require_dist3 = __commonJS({
|
|
|
20909
21864
|
Object.defineProperty(exports2, "detectTlsValidationDisabled", { enumerable: true, get: function() {
|
|
20910
21865
|
return tls_detector_1.detectTlsValidationDisabled;
|
|
20911
21866
|
} });
|
|
21867
|
+
var cors_detector_1 = require_cors_detector();
|
|
21868
|
+
Object.defineProperty(exports2, "detectCorsWildcardCredentials", { enumerable: true, get: function() {
|
|
21869
|
+
return cors_detector_1.detectCorsWildcardCredentials;
|
|
21870
|
+
} });
|
|
21871
|
+
var jwt_detector_1 = require_jwt_detector();
|
|
21872
|
+
Object.defineProperty(exports2, "detectJwtInsecureOptions", { enumerable: true, get: function() {
|
|
21873
|
+
return jwt_detector_1.detectJwtInsecureOptions;
|
|
21874
|
+
} });
|
|
21875
|
+
var jsx_xss_detector_1 = require_jsx_xss_detector();
|
|
21876
|
+
Object.defineProperty(exports2, "detectDangerouslySetInnerHtml", { enumerable: true, get: function() {
|
|
21877
|
+
return jsx_xss_detector_1.detectDangerouslySetInnerHtml;
|
|
21878
|
+
} });
|
|
21879
|
+
var env_typo_detector_1 = require_env_typo_detector();
|
|
21880
|
+
Object.defineProperty(exports2, "detectProcessEnvTypo", { enumerable: true, get: function() {
|
|
21881
|
+
return env_typo_detector_1.detectProcessEnvTypo;
|
|
21882
|
+
} });
|
|
21883
|
+
var maintainability_detector_1 = require_maintainability_detector();
|
|
21884
|
+
Object.defineProperty(exports2, "detectMaintainabilityIssues", { enumerable: true, get: function() {
|
|
21885
|
+
return maintainability_detector_1.detectMaintainabilityIssues;
|
|
21886
|
+
} });
|
|
21887
|
+
var console_in_production_detector_1 = require_console_in_production_detector();
|
|
21888
|
+
Object.defineProperty(exports2, "detectConsoleInProduction", { enumerable: true, get: function() {
|
|
21889
|
+
return console_in_production_detector_1.detectConsoleInProduction;
|
|
21890
|
+
} });
|
|
21891
|
+
var todo_comment_detector_1 = require_todo_comment_detector();
|
|
21892
|
+
Object.defineProperty(exports2, "detectStrayTodoComments", { enumerable: true, get: function() {
|
|
21893
|
+
return todo_comment_detector_1.detectStrayTodoComments;
|
|
21894
|
+
} });
|
|
20912
21895
|
var orchestrator_1 = require_orchestrator();
|
|
20913
21896
|
Object.defineProperty(exports2, "runFullAnalysis", { enumerable: true, get: function() {
|
|
20914
21897
|
return orchestrator_1.runFullAnalysis;
|
|
@@ -20921,8 +21904,8 @@ var require_package = __commonJS({
|
|
|
20921
21904
|
"package.json"(exports2, module2) {
|
|
20922
21905
|
module2.exports = {
|
|
20923
21906
|
name: "technical-debt-radar",
|
|
20924
|
-
version: "1.
|
|
20925
|
-
description: "Stop Node.js production crashes before merge.
|
|
21907
|
+
version: "1.17.0",
|
|
21908
|
+
description: "Stop Node.js production crashes before merge. 74 detection rules across 5 categories.",
|
|
20926
21909
|
bin: {
|
|
20927
21910
|
radar: "dist/index.js",
|
|
20928
21911
|
"technical-debt-radar": "dist/index.js"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "technical-debt-radar",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Stop Node.js production crashes before merge.
|
|
3
|
+
"version": "1.17.0",
|
|
4
|
+
"description": "Stop Node.js production crashes before merge. 74 detection rules across 5 categories.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"radar": "dist/index.js",
|
|
7
7
|
"technical-debt-radar": "dist/index.js"
|