@runa-ai/runa-cli 0.6.0 → 0.7.1
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/{build-BXUJKYHC.js → build-HUDIP6KU.js} +153 -164
- package/dist/{cache-H63JKFYH.js → cache-N7WNPEYF.js} +2 -3
- package/dist/check-LOMVIRHX.js +12 -0
- package/dist/{chunk-HPYJPB5Y.js → chunk-2APB25TT.js} +44 -10
- package/dist/chunk-3WDV32GA.js +33 -0
- package/dist/chunk-5FT3F36G.js +59 -0
- package/dist/{chunk-7QV7U6NI.js → chunk-6FAU4IGR.js} +2 -1
- package/dist/{chunk-CE3DEYFT.js → chunk-7B5C6U2K.js} +2 -208
- package/dist/{chunk-GOGRLQNP.js → chunk-AFY3TX4I.js} +1 -1
- package/dist/{chunk-KWX3JHCY.js → chunk-AKZAN4BC.js} +6 -1
- package/dist/{chunk-XJBQINSA.js → chunk-CCW3PLQY.js} +2 -2
- package/dist/{chunk-IBVVGH6X.js → chunk-EMB6IZFT.js} +17 -4
- package/dist/chunk-FHG3ILE4.js +2011 -0
- package/dist/{chunk-22CS6EMA.js → chunk-H2AHNI75.js} +1 -1
- package/dist/{chunk-UU55OH7P.js → chunk-KE6QJBZG.js} +2 -3
- package/dist/{check-6AB5NGWK.js → chunk-QM53IQHM.js} +14 -12
- package/dist/{chunk-RRGQCUKT.js → chunk-WJXC4MVY.js} +30 -3
- package/dist/chunk-XDCHRVE3.js +215 -0
- package/dist/{chunk-P7U52PBY.js → chunk-Z4Z5DNW4.js} +49 -2
- package/dist/{ci-V3PIG2GI.js → ci-XY6IKEDC.js} +1938 -238
- package/dist/cli/contract-output.d.ts +1 -0
- package/dist/{cli-GFRZCJQR.js → cli-UZA4RBNQ.js} +216 -173
- package/dist/commands/build/actors/validate.d.ts +2 -0
- package/dist/commands/check/commands/check.d.ts +8 -3
- package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +12 -6
- package/dist/commands/ci/machine/actors/db/production-preview.d.ts +10 -0
- package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +77 -0
- package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +11 -0
- package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +9 -1
- package/dist/commands/ci/machine/commands/machine-runner.d.ts +2 -0
- package/dist/commands/ci/machine/formatters/sections/production-schema-status.d.ts +30 -0
- package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +3 -3
- package/dist/commands/ci/machine/helpers.d.ts +8 -0
- package/dist/commands/ci/machine/machine.d.ts +57 -4
- package/dist/commands/ci/machine/types.d.ts +2 -0
- package/dist/commands/ci/utils/execa-helpers.d.ts +1 -0
- package/dist/commands/db/commands/db-sync/error-classifier.d.ts +9 -0
- package/dist/commands/dev/actors/index.d.ts +5 -0
- package/dist/commands/dev/actors/tables-manifest.d.ts +16 -0
- package/dist/commands/dev/contract.d.ts +1 -1
- package/dist/commands/dev/guards.d.ts +24 -0
- package/dist/commands/dev/machine.d.ts +22 -3
- package/dist/commands/dev/types.d.ts +2 -0
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/inject-test-attrs/defaults.d.ts +9 -0
- package/dist/commands/template-check/commands/template-check.d.ts +1 -0
- package/dist/commands/template-check/contract.d.ts +1 -0
- package/dist/commands/utils/machine-state-logging.d.ts +20 -0
- package/dist/commands/utils/repo-root.d.ts +2 -0
- package/dist/constants/versions.d.ts +1 -1
- package/dist/{db-HR7CREX2.js → db-Q3GF7JWP.js} +518 -2234
- package/dist/{dev-A7RW6XQV.js → dev-5YXNPTCJ.js} +168 -49
- package/dist/doctor-MZLOA53G.js +44 -0
- package/dist/{env-B47Z4747.js → env-GMB3THRG.js} +6 -7
- package/dist/{env-files-K2C7O7L5.js → env-files-2UIUYLLR.js} +2 -2
- package/dist/{error-handler-4EYSDOSE.js → error-handler-HEXBRNVV.js} +2 -2
- package/dist/{hotfix-CULKKMGS.js → hotfix-NDTPY2T4.js} +4 -4
- package/dist/index.js +4 -4
- package/dist/{init-ELK5QCWR.js → init-U4VCRHTD.js} +5 -6
- package/dist/{inject-test-attrs-Y5UD5P7Q.js → inject-test-attrs-P44BVTQS.js} +5 -18
- package/dist/{link-C43JRZWY.js → link-VSNDVZZD.js} +2 -3
- package/dist/manifest-TMFLESHW.js +19 -0
- package/dist/{risk-detector-BXUY2WKS.js → risk-detector-4U6ZJ2G5.js} +1 -1
- package/dist/{risk-detector-core-O7I7SPR7.js → risk-detector-core-TK4OAI3N.js} +2 -2
- package/dist/{risk-detector-plpgsql-SGMVKYJP.js → risk-detector-plpgsql-HWKS4OLR.js} +37 -7
- package/dist/{status-IJ4ZWHMX.js → status-UTKS63AB.js} +2 -3
- package/dist/{telemetry-FN7V727Y.js → telemetry-P56UBLZ2.js} +2 -3
- package/dist/{template-check-PNG5NQ5H.js → template-check-FFJVDLBF.js} +63 -35
- package/dist/{test-QYXE5UVW.js → test-V4KQL574.js} +34 -10
- package/dist/{test-gen-QPWOIEHU.js → test-gen-FS4CEY3P.js} +2 -3
- package/dist/{upgrade-3SLWVNAC.js → upgrade-7TWORWBV.js} +18 -6
- package/dist/{validate-SM4PXPS7.js → validate-CAAW4Y44.js} +2 -3
- package/dist/{vuln-check-TYQNEFS7.js → vuln-check-6CMNPSBR.js} +3 -4
- package/dist/{vuln-checker-2QXGN5YT.js → vuln-checker-EJJTNDNE.js} +413 -140
- package/dist/{watch-UCDVOQAH.js → watch-PNTKZYFB.js} +1 -1
- package/dist/{workflow-ZB5Q2PFY.js → workflow-H75N4BXX.js} +3 -4
- package/package.json +2 -2
- package/dist/chunk-JT5SUTWE.js +0 -9
- package/dist/chunk-M47WJJVS.js +0 -71
- package/dist/manifest-2NOQ2IMK.js +0 -32
- package/dist/{chunk-MNPMZERI.js → chunk-644FVGIQ.js} +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { CLI_VERSION } from './chunk-
|
|
3
|
+
import { CLI_VERSION } from './chunk-AFY3TX4I.js';
|
|
4
4
|
import { init_esm_shims } from './chunk-VRXHCR5K.js';
|
|
5
5
|
import { glob } from 'glob';
|
|
6
6
|
import { exec } from 'child_process';
|
|
@@ -499,6 +499,10 @@ var TENANT_ISOLATION_PATTERNS = [
|
|
|
499
499
|
/\(\s*auth\.jwt\s*\(\)\s*->>?\s*['"][^'"]+['"]\s*\)\s*::/i
|
|
500
500
|
];
|
|
501
501
|
var SYSTEM_TABLE_PATTERNS = ["_", "schema_migrations", "drizzle_migrations"];
|
|
502
|
+
var SUPABASE_STORAGE_TABLES = ["storage.objects", "storage.buckets"];
|
|
503
|
+
function isStorageTable(tableName) {
|
|
504
|
+
return SUPABASE_STORAGE_TABLES.includes(tableName.toLowerCase());
|
|
505
|
+
}
|
|
502
506
|
function isPgTableCall(callExpr) {
|
|
503
507
|
const expression = callExpr.getExpression();
|
|
504
508
|
if (Node.isIdentifier(expression)) {
|
|
@@ -753,6 +757,15 @@ function checkTableForIssues(table, tablesWithPolicies, policies) {
|
|
|
753
757
|
const fullTableName = `${table.schema}.${table.name}`;
|
|
754
758
|
if (isSystemTable(table.name)) return findings;
|
|
755
759
|
if (!tablesWithPolicies.has(fullTableName)) {
|
|
760
|
+
if (isStorageTable(fullTableName)) {
|
|
761
|
+
findings.push({
|
|
762
|
+
...createMissingRLSFinding(table, fullTableName),
|
|
763
|
+
severity: "medium",
|
|
764
|
+
description: `Storage table "${fullTableName}" \u2014 Supabase provides default storage policies. Custom RLS may still be needed.`,
|
|
765
|
+
confidence: 0.4
|
|
766
|
+
});
|
|
767
|
+
return findings;
|
|
768
|
+
}
|
|
756
769
|
findings.push(createMissingRLSFinding(table, fullTableName));
|
|
757
770
|
return findings;
|
|
758
771
|
}
|
|
@@ -775,7 +788,17 @@ var RLSAnalyzer = class {
|
|
|
775
788
|
const tableFindings = tables.flatMap(
|
|
776
789
|
(table) => checkTableForIssues(table, tablesWithPolicies, policies)
|
|
777
790
|
);
|
|
778
|
-
const policyFindings = policies.filter(isPermissivePolicy).map(
|
|
791
|
+
const policyFindings = policies.filter(isPermissivePolicy).map((policy) => {
|
|
792
|
+
if (isStorageTable(policy.table) && /SELECT/i.test(policy.operation)) {
|
|
793
|
+
return {
|
|
794
|
+
...createPermissiveFinding(policy),
|
|
795
|
+
severity: "info",
|
|
796
|
+
description: `Policy "${policy.name}" on "${policy.table}" allows public SELECT. This is common for public file downloads. Verify this is intentional.`,
|
|
797
|
+
confidence: 0.3
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
return createPermissiveFinding(policy);
|
|
801
|
+
});
|
|
779
802
|
return [...tableFindings, ...policyFindings];
|
|
780
803
|
}
|
|
781
804
|
};
|
|
@@ -784,6 +807,7 @@ var RLSAnalyzer = class {
|
|
|
784
807
|
init_esm_shims();
|
|
785
808
|
var MAX_LINE_LENGTH = 2e3;
|
|
786
809
|
var MAX_FILE_SIZE = 1024 * 1024;
|
|
810
|
+
var SECRET_SCAN_SKIPPED_RULE_ID = "secret/scan-skipped";
|
|
787
811
|
var SECRET_PATTERNS = [
|
|
788
812
|
// AWS
|
|
789
813
|
{
|
|
@@ -1483,6 +1507,52 @@ function createBase64SecretFinding(line, lineNumber, filePath, base64Match, secr
|
|
|
1483
1507
|
confidence: 0.85
|
|
1484
1508
|
};
|
|
1485
1509
|
}
|
|
1510
|
+
function createSkippedLineScanFinding(filePath, firstLineNumber, skippedLineCount) {
|
|
1511
|
+
return {
|
|
1512
|
+
ruleId: SECRET_SCAN_SKIPPED_RULE_ID,
|
|
1513
|
+
severity: "low",
|
|
1514
|
+
title: "Secret Scan Partially Skipped",
|
|
1515
|
+
description: `Secret scan skipped ${skippedLineCount} overlong line(s) exceeding ${MAX_LINE_LENGTH} characters.`,
|
|
1516
|
+
location: {
|
|
1517
|
+
file: filePath,
|
|
1518
|
+
line: firstLineNumber,
|
|
1519
|
+
column: 0
|
|
1520
|
+
},
|
|
1521
|
+
fix: {
|
|
1522
|
+
description: "Split minified or generated content so the scanner can inspect each line safely"
|
|
1523
|
+
},
|
|
1524
|
+
metadata: {
|
|
1525
|
+
skippedLineCount,
|
|
1526
|
+
maxLineLength: MAX_LINE_LENGTH
|
|
1527
|
+
},
|
|
1528
|
+
cweId: CWE.HARDCODED_CREDENTIALS,
|
|
1529
|
+
owaspCategory: OWASP_2021.CRYPTO_FAILURES,
|
|
1530
|
+
confidence: 1
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
function createSkippedFileScanFinding(filePath, sizeBytes) {
|
|
1534
|
+
return {
|
|
1535
|
+
ruleId: SECRET_SCAN_SKIPPED_RULE_ID,
|
|
1536
|
+
severity: "low",
|
|
1537
|
+
title: "Secret Scan Skipped Large File",
|
|
1538
|
+
description: `Secret scan skipped a file larger than ${MAX_FILE_SIZE} bytes.`,
|
|
1539
|
+
location: {
|
|
1540
|
+
file: filePath,
|
|
1541
|
+
line: 1,
|
|
1542
|
+
column: 0
|
|
1543
|
+
},
|
|
1544
|
+
fix: {
|
|
1545
|
+
description: "Reduce file size or split generated artifacts so secret scanning can inspect the content"
|
|
1546
|
+
},
|
|
1547
|
+
metadata: {
|
|
1548
|
+
sizeBytes,
|
|
1549
|
+
maxFileSize: MAX_FILE_SIZE
|
|
1550
|
+
},
|
|
1551
|
+
cweId: CWE.HARDCODED_CREDENTIALS,
|
|
1552
|
+
owaspCategory: OWASP_2021.CRYPTO_FAILURES,
|
|
1553
|
+
confidence: 1
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1486
1556
|
function collectBase64SecretFindings(line, lineNumber, filePath) {
|
|
1487
1557
|
const findings = [];
|
|
1488
1558
|
for (const base64Match of line.matchAll(BASE64_SECRET_PATTERN)) {
|
|
@@ -1508,15 +1578,26 @@ function scanLineForSecrets(line, lineNumber, filePath) {
|
|
|
1508
1578
|
async function scanFileForSecrets(filePath) {
|
|
1509
1579
|
const stats = await fs.stat(filePath);
|
|
1510
1580
|
if (stats.size > MAX_FILE_SIZE) {
|
|
1511
|
-
return [];
|
|
1581
|
+
return [createSkippedFileScanFinding(filePath, stats.size)];
|
|
1512
1582
|
}
|
|
1513
1583
|
const content = await fs.readFile(filePath, "utf-8");
|
|
1514
1584
|
const lines = content.split("\n");
|
|
1515
1585
|
const findings = [];
|
|
1586
|
+
let skippedLineCount = 0;
|
|
1587
|
+
let firstSkippedLineNumber = null;
|
|
1516
1588
|
for (let i = 0; i < lines.length; i++) {
|
|
1517
|
-
const
|
|
1589
|
+
const lineNumber = i + 1;
|
|
1590
|
+
if (lines[i].length > MAX_LINE_LENGTH) {
|
|
1591
|
+
skippedLineCount += 1;
|
|
1592
|
+
firstSkippedLineNumber ??= lineNumber;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const lineFindings = scanLineForSecrets(lines[i], lineNumber, filePath);
|
|
1518
1596
|
findings.push(...lineFindings);
|
|
1519
1597
|
}
|
|
1598
|
+
if (firstSkippedLineNumber !== null) {
|
|
1599
|
+
findings.push(createSkippedLineScanFinding(filePath, firstSkippedLineNumber, skippedLineCount));
|
|
1600
|
+
}
|
|
1520
1601
|
const multiLineFindings = scanMultiLineSecrets(content, filePath);
|
|
1521
1602
|
findings.push(...multiLineFindings);
|
|
1522
1603
|
return findings;
|
|
@@ -1700,10 +1781,10 @@ function hasUserInputInterpolation(text) {
|
|
|
1700
1781
|
for (const expr of interpolations) {
|
|
1701
1782
|
const innerExpr = expr.slice(2, -1).trim();
|
|
1702
1783
|
if (/^\d+$/.test(innerExpr)) continue;
|
|
1784
|
+
if (SAFE_VARIABLE_PATTERNS.some((p) => p.test(text))) continue;
|
|
1703
1785
|
if (USER_INPUT_PATTERNS.some((p) => p.test(innerExpr))) {
|
|
1704
1786
|
return true;
|
|
1705
1787
|
}
|
|
1706
|
-
if (SAFE_VARIABLE_PATTERNS.some((p) => p.test(text))) ;
|
|
1707
1788
|
}
|
|
1708
1789
|
return false;
|
|
1709
1790
|
}
|
|
@@ -1720,6 +1801,116 @@ function hasArrayBasedSqlBuilding(text) {
|
|
|
1720
1801
|
];
|
|
1721
1802
|
return arrayBuildPatterns.some((p) => p.test(text));
|
|
1722
1803
|
}
|
|
1804
|
+
function buildDefinitionPositionsFromNodes(nodes) {
|
|
1805
|
+
return new Set(nodes.map((node) => `${node.getSourceFile().getFilePath()}:${node.getPos()}`));
|
|
1806
|
+
}
|
|
1807
|
+
function hasDefinitionMatch(node, definitionPositions) {
|
|
1808
|
+
if (!Node.isIdentifier(node)) return false;
|
|
1809
|
+
const nodeDefinitions = node.getDefinitions().map((definition) => definition.getNode());
|
|
1810
|
+
return nodeDefinitions.some(
|
|
1811
|
+
(definitionNode) => definitionPositions.has(
|
|
1812
|
+
`${definitionNode.getSourceFile().getFilePath()}:${definitionNode.getPos()}`
|
|
1813
|
+
)
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
function buildJoinedArrayBuilderRisk(node, sourceFile) {
|
|
1817
|
+
const joinTarget = getIdentifierFromJoinCall(node);
|
|
1818
|
+
if (!joinTarget) return null;
|
|
1819
|
+
const scopeNode = node.getFirstAncestor((ancestor) => Node.isBlock(ancestor) || Node.isSourceFile(ancestor)) ?? sourceFile;
|
|
1820
|
+
if (isDynamicArrayPushInScope(scopeNode.getText(), joinTarget.getText())) {
|
|
1821
|
+
return {
|
|
1822
|
+
confidence: 0.6,
|
|
1823
|
+
description: "SQL query variable may be built dynamically from array concatenation."
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
const arrayDefinitionPositions = getDefinitionPositions(joinTarget);
|
|
1827
|
+
if (arrayDefinitionPositions.size === 0) return null;
|
|
1828
|
+
if (hasDynamicPushArgumentFromDefinition(
|
|
1829
|
+
sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression),
|
|
1830
|
+
arrayDefinitionPositions
|
|
1831
|
+
)) {
|
|
1832
|
+
return {
|
|
1833
|
+
confidence: 0.6,
|
|
1834
|
+
description: "SQL query variable may be built dynamically from array concatenation."
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
return null;
|
|
1838
|
+
}
|
|
1839
|
+
function getIdentifierFromJoinCall(node) {
|
|
1840
|
+
if (!Node.isCallExpression(node)) return null;
|
|
1841
|
+
const expression = node.getExpression();
|
|
1842
|
+
if (!Node.isPropertyAccessExpression(expression) || expression.getName() !== "join") return null;
|
|
1843
|
+
const joinTarget = expression.getExpression();
|
|
1844
|
+
return Node.isIdentifier(joinTarget) ? joinTarget : null;
|
|
1845
|
+
}
|
|
1846
|
+
function isDynamicArrayPushInScope(scopedText, arrayName) {
|
|
1847
|
+
return hasDynamicArrayPush(scopedText, arrayName);
|
|
1848
|
+
}
|
|
1849
|
+
function getDefinitionPositions(identifier) {
|
|
1850
|
+
return buildDefinitionPositionsFromNodes(
|
|
1851
|
+
identifier.getDefinitions().map((definition) => definition.getNode()).filter((node) => node !== void 0)
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
function hasDynamicPushArgumentFromDefinition(callExpressions, definitionPositions) {
|
|
1855
|
+
for (const call of callExpressions) {
|
|
1856
|
+
const pushExpression = call.getExpression();
|
|
1857
|
+
if (!Node.isPropertyAccessExpression(pushExpression) || pushExpression.getName() !== "push") {
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
const pushTarget = pushExpression.getExpression();
|
|
1861
|
+
if (!Node.isIdentifier(pushTarget) || !hasDefinitionMatch(pushTarget, definitionPositions)) {
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
const pushArg = call.getArguments()[0];
|
|
1865
|
+
if (!pushArg) continue;
|
|
1866
|
+
if ((Node.isTemplateExpression(pushArg) || Node.isBinaryExpression(pushArg)) && hasUserInputInterpolation(pushArg.getText())) {
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return false;
|
|
1871
|
+
}
|
|
1872
|
+
function hasDynamicArrayPush(scopedText, arrayName) {
|
|
1873
|
+
return new RegExp(`\\b${arrayName}\\.push\\s*\\(\\s*\`[^\\\`]*\\$\\{`, "m").test(scopedText) || new RegExp(`\\b${arrayName}\\.push\\s*\\(\\s*['"][^'"]*['"]\\s*\\+`, "m").test(scopedText);
|
|
1874
|
+
}
|
|
1875
|
+
function collectIdentifierDefinitionRisk(definitionNode, sourceFile) {
|
|
1876
|
+
if (!Node.isVariableDeclaration(definitionNode)) return null;
|
|
1877
|
+
const initializer = definitionNode.getInitializer();
|
|
1878
|
+
if (!initializer) return null;
|
|
1879
|
+
const risk = classifyDynamicSqlNode(initializer);
|
|
1880
|
+
if (risk) return risk;
|
|
1881
|
+
return buildJoinedArrayBuilderRisk(initializer, sourceFile);
|
|
1882
|
+
}
|
|
1883
|
+
function detectAssignmentRisk(sourceFile, identifierName, definitionPositions) {
|
|
1884
|
+
for (const expression of sourceFile.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
1885
|
+
if (expression.getOperatorToken().getKind() !== SyntaxKind.EqualsToken) continue;
|
|
1886
|
+
const left = expression.getLeft();
|
|
1887
|
+
if (!Node.isIdentifier(left) || left.getText() !== identifierName) continue;
|
|
1888
|
+
if (!hasDefinitionMatch(left, definitionPositions)) continue;
|
|
1889
|
+
const risk = classifyDynamicSqlNode(expression.getRight());
|
|
1890
|
+
if (risk) return risk;
|
|
1891
|
+
const joinRisk = buildJoinedArrayBuilderRisk(expression.getRight(), sourceFile);
|
|
1892
|
+
if (joinRisk) return joinRisk;
|
|
1893
|
+
}
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
function detectIdentifierJoinPattern(firstArg, identifierName, sourceFile) {
|
|
1897
|
+
const scopeNode = firstArg.getFirstAncestor(
|
|
1898
|
+
(ancestor) => Node.isBlock(ancestor) || Node.isSourceFile(ancestor)
|
|
1899
|
+
) ?? sourceFile;
|
|
1900
|
+
const scopedText = scopeNode.getText();
|
|
1901
|
+
const identifierJoinPattern = new RegExp(
|
|
1902
|
+
`(?:const|let|var)\\s+${identifierName}\\s*=\\s*(\\w+)\\.join\\s*\\(`,
|
|
1903
|
+
"m"
|
|
1904
|
+
);
|
|
1905
|
+
const joinMatch = scopedText.match(identifierJoinPattern);
|
|
1906
|
+
if (!joinMatch?.[1]) return null;
|
|
1907
|
+
const arrayName = joinMatch[1];
|
|
1908
|
+
if (!hasDynamicArrayPush(scopedText, arrayName)) return null;
|
|
1909
|
+
return {
|
|
1910
|
+
confidence: 0.6,
|
|
1911
|
+
description: "SQL query variable may be built dynamically from array concatenation."
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1723
1914
|
function isDynamicQueryBuilder(text) {
|
|
1724
1915
|
const builderPatterns = [
|
|
1725
1916
|
/buildQuery\s*\(/i,
|
|
@@ -1731,6 +1922,183 @@ function isDynamicQueryBuilder(text) {
|
|
|
1731
1922
|
];
|
|
1732
1923
|
return builderPatterns.some((p) => p.test(text));
|
|
1733
1924
|
}
|
|
1925
|
+
var SQL_EXECUTION_METHODS = ["query", "execute", "raw", "unsafe", "sql", "runQuery"];
|
|
1926
|
+
var DB_CALLER_PATTERN = /\b(?:db|client|pool|connection|knex|prisma|drizzle|supabase|pg|postgres|conn|database)\b/i;
|
|
1927
|
+
var SQL_SAFE_PATTERNS = [/^sql`/, /^sql\.raw\(sql`/, /\$\d+/, /\?/, /:[\w]+/];
|
|
1928
|
+
var CLIENT_FILE_PATTERNS = [/\/hooks\//i, /\/components\//i, /use[A-Z]\w*\.tsx?$/];
|
|
1929
|
+
var ROUTE_METHODS = [
|
|
1930
|
+
"get",
|
|
1931
|
+
"post",
|
|
1932
|
+
"put",
|
|
1933
|
+
"patch",
|
|
1934
|
+
"delete",
|
|
1935
|
+
"app.get",
|
|
1936
|
+
"app.post",
|
|
1937
|
+
"app.put",
|
|
1938
|
+
"app.patch",
|
|
1939
|
+
"app.delete",
|
|
1940
|
+
"router.get",
|
|
1941
|
+
"router.post"
|
|
1942
|
+
];
|
|
1943
|
+
var AUTH_MIDDLEWARE_PATTERNS = [
|
|
1944
|
+
/\bauth(?:enticate)?(?:Middleware)?\b/i,
|
|
1945
|
+
/\bisAuthenticated\b/i,
|
|
1946
|
+
/\brequireAuth\b/i,
|
|
1947
|
+
/\bprotect(?:ed)?(?:Route)?\b/i,
|
|
1948
|
+
/\bverify(?:Token|JWT|Session)\b/i,
|
|
1949
|
+
/\bcheck(?:Auth|Token|Session)\b/i,
|
|
1950
|
+
/\bguard\b/i,
|
|
1951
|
+
/\bmiddleware\(/i,
|
|
1952
|
+
/\buse\s*\(\s*auth/i
|
|
1953
|
+
];
|
|
1954
|
+
var PUBLIC_ROUTE_PATTERNS = [
|
|
1955
|
+
/\/public\//i,
|
|
1956
|
+
/\/health(?:check)?(?:\/|$)/i,
|
|
1957
|
+
/\/(?:login|logout|signin|signout)(?:\/|$)/i,
|
|
1958
|
+
/\/(?:signup|register)(?:\/|$)/i,
|
|
1959
|
+
/\/(?:forgot|reset)-?password/i,
|
|
1960
|
+
/\/callback(?:\/|$)/i,
|
|
1961
|
+
/\/webhook(?:s)?(?:\/|$)/i,
|
|
1962
|
+
/\/api\/v\d+\/public\//i,
|
|
1963
|
+
/\/\.well-known\//i,
|
|
1964
|
+
/\/robots\.txt/i,
|
|
1965
|
+
/\/favicon/i,
|
|
1966
|
+
/\/status(?:\/|$)/i,
|
|
1967
|
+
/\/ping(?:\/|$)/i,
|
|
1968
|
+
/\/version(?:\/|$)/i
|
|
1969
|
+
];
|
|
1970
|
+
function isSqlExecutionCall(expressionText) {
|
|
1971
|
+
const isSqlMethod = SQL_EXECUTION_METHODS.some(
|
|
1972
|
+
(method) => expressionText.endsWith(`.${method}`) || expressionText === method
|
|
1973
|
+
);
|
|
1974
|
+
const isDbExec = expressionText.endsWith(".exec") && DB_CALLER_PATTERN.test(expressionText);
|
|
1975
|
+
return isSqlMethod || isDbExec;
|
|
1976
|
+
}
|
|
1977
|
+
function isSafeSqlArgument(argText) {
|
|
1978
|
+
return SQL_SAFE_PATTERNS.some((pattern) => pattern.test(argText));
|
|
1979
|
+
}
|
|
1980
|
+
function classifyDynamicSqlNode(node) {
|
|
1981
|
+
const text = node.getText();
|
|
1982
|
+
if (Node.isTemplateExpression(node) && hasUserInputInterpolation(text)) {
|
|
1983
|
+
return {
|
|
1984
|
+
confidence: 0.9,
|
|
1985
|
+
description: "Template literal with user input in SQL query."
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
if (Node.isBinaryExpression(node) && hasUserInputInterpolation(text)) {
|
|
1989
|
+
return {
|
|
1990
|
+
confidence: 0.85,
|
|
1991
|
+
description: "String concatenation with user input in SQL query."
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
if (isDynamicQueryBuilder(text)) {
|
|
1995
|
+
return {
|
|
1996
|
+
confidence: 0.7,
|
|
1997
|
+
description: "SQL query built by function that may use string concatenation."
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
if (hasArrayBasedSqlBuilding(text)) {
|
|
2001
|
+
return {
|
|
2002
|
+
confidence: 0.6,
|
|
2003
|
+
description: "SQL query variable may be built dynamically from array concatenation."
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
return null;
|
|
2007
|
+
}
|
|
2008
|
+
function findIdentifierSqlConstruction(firstArg) {
|
|
2009
|
+
if (!Node.isIdentifier(firstArg)) return null;
|
|
2010
|
+
const identifierName = firstArg.getText();
|
|
2011
|
+
const definitionNodes = firstArg.getDefinitions().map((definition) => definition.getNode()).filter((node) => node !== void 0);
|
|
2012
|
+
const sourceFile = firstArg.getSourceFile();
|
|
2013
|
+
const definitionPositions = buildDefinitionPositionsFromNodes(definitionNodes);
|
|
2014
|
+
for (const definitionNode of definitionNodes) {
|
|
2015
|
+
const risk = collectIdentifierDefinitionRisk(definitionNode, sourceFile);
|
|
2016
|
+
if (risk) return risk;
|
|
2017
|
+
}
|
|
2018
|
+
const assignmentRisk = detectAssignmentRisk(sourceFile, identifierName, definitionPositions);
|
|
2019
|
+
if (assignmentRisk) return assignmentRisk;
|
|
2020
|
+
const directMatchRisk = detectIdentifierJoinPattern(firstArg, identifierName, sourceFile);
|
|
2021
|
+
if (directMatchRisk) {
|
|
2022
|
+
return directMatchRisk;
|
|
2023
|
+
}
|
|
2024
|
+
return null;
|
|
2025
|
+
}
|
|
2026
|
+
function evaluateSqlInjectionRisk(firstArg) {
|
|
2027
|
+
const argText = firstArg.getText();
|
|
2028
|
+
if (isSafeSqlArgument(argText)) {
|
|
2029
|
+
return { isDangerous: false, confidence: 0, description: "" };
|
|
2030
|
+
}
|
|
2031
|
+
const directRisk = classifyDynamicSqlNode(firstArg);
|
|
2032
|
+
if (directRisk) {
|
|
2033
|
+
return {
|
|
2034
|
+
isDangerous: true,
|
|
2035
|
+
confidence: directRisk.confidence,
|
|
2036
|
+
description: directRisk.description
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
const identifierRisk = findIdentifierSqlConstruction(firstArg);
|
|
2040
|
+
if (identifierRisk) {
|
|
2041
|
+
return {
|
|
2042
|
+
isDangerous: true,
|
|
2043
|
+
confidence: identifierRisk.confidence,
|
|
2044
|
+
description: identifierRisk.description
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
return { isDangerous: false, confidence: 0, description: "" };
|
|
2048
|
+
}
|
|
2049
|
+
function isClientSideRouteFile(filePath) {
|
|
2050
|
+
return CLIENT_FILE_PATTERNS.some((pattern) => pattern.test(filePath));
|
|
2051
|
+
}
|
|
2052
|
+
function isRouteMethodCall(expressionText) {
|
|
2053
|
+
return ROUTE_METHODS.some(
|
|
2054
|
+
(method) => expressionText === method || expressionText.endsWith(`.${method.split(".").pop()}`)
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
function hasHookOrEventWrapper(node) {
|
|
2058
|
+
let current = node.getParent();
|
|
2059
|
+
let depth = 0;
|
|
2060
|
+
while (current && depth < 10) {
|
|
2061
|
+
if (Node.isCallExpression(current)) {
|
|
2062
|
+
const callee = current.getExpression().getText();
|
|
2063
|
+
if (/^use[A-Z]/.test(callee)) return true;
|
|
2064
|
+
}
|
|
2065
|
+
if (Node.isPropertyAssignment(current)) {
|
|
2066
|
+
const name = current.getName();
|
|
2067
|
+
if (/^on[A-Z]/.test(name)) return true;
|
|
2068
|
+
}
|
|
2069
|
+
current = current.getParent();
|
|
2070
|
+
depth += 1;
|
|
2071
|
+
}
|
|
2072
|
+
return false;
|
|
2073
|
+
}
|
|
2074
|
+
function hasAuthMiddlewareArg(args) {
|
|
2075
|
+
const middlewareArgs = args.slice(1, -1);
|
|
2076
|
+
return middlewareArgs.some((arg) => {
|
|
2077
|
+
const argText = arg.getText();
|
|
2078
|
+
return AUTH_MIDDLEWARE_PATTERNS.some((pattern) => pattern.test(argText));
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
function hasInlineAuthCheck(lastArg, fullText) {
|
|
2082
|
+
const handlerText = lastArg.getText();
|
|
2083
|
+
const handlerPatterns = [
|
|
2084
|
+
/headers\.authorization/i,
|
|
2085
|
+
/getToken\s*\(/i,
|
|
2086
|
+
/session\./i,
|
|
2087
|
+
/req\.user\b/i,
|
|
2088
|
+
/ctx\.(?:user|auth|session)\b/i,
|
|
2089
|
+
/throw.*(?:Unauthorized|401)/i
|
|
2090
|
+
];
|
|
2091
|
+
return handlerPatterns.some((pattern) => pattern.test(handlerText)) || AUTH_MIDDLEWARE_PATTERNS.some((pattern) => pattern.test(fullText));
|
|
2092
|
+
}
|
|
2093
|
+
function isPublicRoute(fullText) {
|
|
2094
|
+
return PUBLIC_ROUTE_PATTERNS.some((pattern) => pattern.test(fullText));
|
|
2095
|
+
}
|
|
2096
|
+
function buildAuthConfidence(expressionText, fullText) {
|
|
2097
|
+
let confidence = 0.4;
|
|
2098
|
+
if (/\/(?:api|admin|user|account|setting|profile)/i.test(fullText)) confidence = 0.6;
|
|
2099
|
+
if (/\.(post|put|patch|delete)\s*\(/i.test(expressionText)) confidence += 0.1;
|
|
2100
|
+
return confidence;
|
|
2101
|
+
}
|
|
1734
2102
|
var TS_RULES = [
|
|
1735
2103
|
// SQL Injection Detection
|
|
1736
2104
|
{
|
|
@@ -1740,77 +2108,27 @@ var TS_RULES = [
|
|
|
1740
2108
|
if (!Node.isCallExpression(node)) return null;
|
|
1741
2109
|
const expression = node.getExpression();
|
|
1742
2110
|
const expressionText = expression.getText();
|
|
1743
|
-
|
|
1744
|
-
const isSqlMethod = sqlMethods.some(
|
|
1745
|
-
(m) => expressionText.endsWith(`.${m}`) || expressionText === m
|
|
1746
|
-
);
|
|
1747
|
-
if (!isSqlMethod) return null;
|
|
2111
|
+
if (!isSqlExecutionCall(expressionText)) return null;
|
|
1748
2112
|
const args = node.getArguments();
|
|
1749
2113
|
if (args.length === 0) return null;
|
|
1750
2114
|
const firstArg = args[0];
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
let confidence = 0.8;
|
|
1769
|
-
let description = "String interpolation in SQL query detected.";
|
|
1770
|
-
if (Node.isTemplateExpression(firstArg)) {
|
|
1771
|
-
if (hasUserInputInterpolation(argText)) {
|
|
1772
|
-
isDangerous = true;
|
|
1773
|
-
confidence = 0.9;
|
|
1774
|
-
description = "Template literal with user input in SQL query.";
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
if (Node.isBinaryExpression(firstArg) && hasUserInputInterpolation(argText)) {
|
|
1778
|
-
isDangerous = true;
|
|
1779
|
-
confidence = 0.85;
|
|
1780
|
-
description = "String concatenation with user input in SQL query.";
|
|
1781
|
-
}
|
|
1782
|
-
if (Node.isIdentifier(firstArg)) {
|
|
1783
|
-
const sourceFile = node.getSourceFile();
|
|
1784
|
-
const fileText = sourceFile.getText();
|
|
1785
|
-
if (hasArrayBasedSqlBuilding(fileText)) {
|
|
1786
|
-
isDangerous = true;
|
|
1787
|
-
confidence = 0.6;
|
|
1788
|
-
description = "SQL query variable may be built dynamically from array concatenation.";
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
if (Node.isCallExpression(firstArg) && isDynamicQueryBuilder(argText)) {
|
|
1792
|
-
isDangerous = true;
|
|
1793
|
-
confidence = 0.7;
|
|
1794
|
-
description = "SQL query built by function that may use string concatenation.";
|
|
1795
|
-
}
|
|
1796
|
-
if (isDangerous) {
|
|
1797
|
-
return {
|
|
1798
|
-
ruleId: "injection/sql",
|
|
1799
|
-
severity: "critical",
|
|
1800
|
-
title: "Potential SQL Injection",
|
|
1801
|
-
description: `${description} Use parameterized queries to prevent SQL injection.`,
|
|
1802
|
-
location: getLocation(node, context.filePath),
|
|
1803
|
-
snippet: getSnippet(node),
|
|
1804
|
-
fix: {
|
|
1805
|
-
description: "Use parameterized queries or Drizzle ORM",
|
|
1806
|
-
replacement: "Use db.select().from(table).where(eq(column, value))"
|
|
1807
|
-
},
|
|
1808
|
-
cweId: CWE.SQL_INJECTION,
|
|
1809
|
-
owaspCategory: OWASP_2021.INJECTION,
|
|
1810
|
-
confidence
|
|
1811
|
-
};
|
|
1812
|
-
}
|
|
1813
|
-
return null;
|
|
2115
|
+
const risk = evaluateSqlInjectionRisk(firstArg);
|
|
2116
|
+
if (!risk.isDangerous) return null;
|
|
2117
|
+
return {
|
|
2118
|
+
ruleId: "injection/sql",
|
|
2119
|
+
severity: "critical",
|
|
2120
|
+
title: "Potential SQL Injection",
|
|
2121
|
+
description: `${risk.description} Use parameterized queries to prevent SQL injection.`,
|
|
2122
|
+
location: getLocation(node, context.filePath),
|
|
2123
|
+
snippet: getSnippet(node),
|
|
2124
|
+
fix: {
|
|
2125
|
+
description: "Use parameterized queries or Drizzle ORM",
|
|
2126
|
+
replacement: "Use db.select().from(table).where(eq(column, value))"
|
|
2127
|
+
},
|
|
2128
|
+
cweId: CWE.SQL_INJECTION,
|
|
2129
|
+
owaspCategory: OWASP_2021.INJECTION,
|
|
2130
|
+
confidence: risk.confidence
|
|
2131
|
+
};
|
|
1814
2132
|
}
|
|
1815
2133
|
},
|
|
1816
2134
|
// XSS Detection
|
|
@@ -1851,6 +2169,17 @@ var TS_RULES = [
|
|
|
1851
2169
|
id: "injection/command",
|
|
1852
2170
|
name: "Command Injection",
|
|
1853
2171
|
detect: (node, context) => {
|
|
2172
|
+
const NON_RUNTIME_PATH_PATTERNS = [
|
|
2173
|
+
/\/scripts\//i,
|
|
2174
|
+
/\/tools\//i,
|
|
2175
|
+
/\/bin\//i,
|
|
2176
|
+
/\.config\./i,
|
|
2177
|
+
/\/__tests__\//i,
|
|
2178
|
+
/\.test\./i,
|
|
2179
|
+
/\.spec\./i,
|
|
2180
|
+
/\/migrations?\//i,
|
|
2181
|
+
/\/seeds?\//i
|
|
2182
|
+
];
|
|
1854
2183
|
if (!Node.isCallExpression(node)) return null;
|
|
1855
2184
|
const expression = node.getExpression();
|
|
1856
2185
|
const expressionText = expression.getText();
|
|
@@ -1870,9 +2199,10 @@ var TS_RULES = [
|
|
|
1870
2199
|
if (args.length === 0) return null;
|
|
1871
2200
|
const firstArg = args[0];
|
|
1872
2201
|
if (Node.isTemplateExpression(firstArg) || Node.isBinaryExpression(firstArg)) {
|
|
2202
|
+
const isNonRuntime = NON_RUNTIME_PATH_PATTERNS.some((p) => p.test(context.filePath));
|
|
1873
2203
|
return {
|
|
1874
2204
|
ruleId: "injection/command",
|
|
1875
|
-
severity: "critical",
|
|
2205
|
+
severity: isNonRuntime ? "medium" : "critical",
|
|
1876
2206
|
title: "Potential Command Injection",
|
|
1877
2207
|
description: "User input in command execution detected. Use parameterized commands or input validation.",
|
|
1878
2208
|
location: getLocation(node, context.filePath),
|
|
@@ -1882,7 +2212,7 @@ var TS_RULES = [
|
|
|
1882
2212
|
},
|
|
1883
2213
|
cweId: CWE.COMMAND_INJECTION,
|
|
1884
2214
|
owaspCategory: OWASP_2021.INJECTION,
|
|
1885
|
-
confidence: 0.7
|
|
2215
|
+
confidence: isNonRuntime ? 0.5 : 0.7
|
|
1886
2216
|
};
|
|
1887
2217
|
}
|
|
1888
2218
|
return null;
|
|
@@ -1926,78 +2256,21 @@ var TS_RULES = [
|
|
|
1926
2256
|
name: "Missing Authentication Check",
|
|
1927
2257
|
detect: (node, context) => {
|
|
1928
2258
|
if (!Node.isCallExpression(node)) return null;
|
|
2259
|
+
if (isClientSideRouteFile(context.filePath)) return null;
|
|
1929
2260
|
const expression = node.getExpression();
|
|
1930
2261
|
const expressionText = expression.getText();
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
"post",
|
|
1934
|
-
"put",
|
|
1935
|
-
"patch",
|
|
1936
|
-
"delete",
|
|
1937
|
-
"app.get",
|
|
1938
|
-
"app.post",
|
|
1939
|
-
"app.put",
|
|
1940
|
-
"app.patch",
|
|
1941
|
-
"app.delete",
|
|
1942
|
-
"router.get",
|
|
1943
|
-
"router.post"
|
|
1944
|
-
];
|
|
1945
|
-
const isRoute = routeMethods.some(
|
|
1946
|
-
(m) => expressionText === m || expressionText.endsWith(`.${m.split(".").pop()}`)
|
|
1947
|
-
);
|
|
1948
|
-
if (!isRoute) return null;
|
|
2262
|
+
if (!isRouteMethodCall(expressionText)) return null;
|
|
2263
|
+
if (hasHookOrEventWrapper(node)) return null;
|
|
1949
2264
|
const args = node.getArguments();
|
|
1950
2265
|
if (args.length < 2) return null;
|
|
1951
2266
|
const fullText = node.getText();
|
|
1952
|
-
const
|
|
1953
|
-
/\bauth(?:enticate)?(?:Middleware)?\b/i,
|
|
1954
|
-
/\bisAuthenticated\b/i,
|
|
1955
|
-
/\brequireAuth\b/i,
|
|
1956
|
-
/\bprotect(?:ed)?(?:Route)?\b/i,
|
|
1957
|
-
/\bverify(?:Token|JWT|Session)\b/i,
|
|
1958
|
-
/\bcheck(?:Auth|Token|Session)\b/i,
|
|
1959
|
-
/\bguard\b/i,
|
|
1960
|
-
/\bmiddleware\(/i,
|
|
1961
|
-
/\buse\s*\(\s*auth/i
|
|
1962
|
-
];
|
|
1963
|
-
const middlewareArgs = args.slice(1, -1);
|
|
1964
|
-
const hasAuthMiddleware = middlewareArgs.some((arg) => {
|
|
1965
|
-
const argText = arg.getText();
|
|
1966
|
-
return authMiddlewarePatterns.some((p) => p.test(argText));
|
|
1967
|
-
});
|
|
2267
|
+
const hasAuthMiddleware = hasAuthMiddlewareArg(args);
|
|
1968
2268
|
const lastArg = args[args.length - 1];
|
|
1969
|
-
const
|
|
1970
|
-
const
|
|
1971
|
-
const
|
|
1972
|
-
const hasAuth = hasAuthMiddleware || hasInlineAuthCheck || hasAuthInFullText;
|
|
1973
|
-
const publicRoutePatterns = [
|
|
1974
|
-
/\/public\//i,
|
|
1975
|
-
/\/health(?:check)?(?:\/|$)/i,
|
|
1976
|
-
/\/(?:login|logout|signin|signout)(?:\/|$)/i,
|
|
1977
|
-
/\/(?:signup|register)(?:\/|$)/i,
|
|
1978
|
-
/\/(?:forgot|reset)-?password/i,
|
|
1979
|
-
/\/callback(?:\/|$)/i,
|
|
1980
|
-
// OAuth callbacks
|
|
1981
|
-
/\/webhook(?:s)?(?:\/|$)/i,
|
|
1982
|
-
// Webhooks often have their own auth
|
|
1983
|
-
/\/api\/v\d+\/public\//i,
|
|
1984
|
-
/\/\.well-known\//i,
|
|
1985
|
-
// OpenID discovery, etc.
|
|
1986
|
-
/\/robots\.txt/i,
|
|
1987
|
-
/\/favicon/i,
|
|
1988
|
-
/\/status(?:\/|$)/i,
|
|
1989
|
-
/\/ping(?:\/|$)/i,
|
|
1990
|
-
/\/version(?:\/|$)/i
|
|
1991
|
-
];
|
|
1992
|
-
const isPublic = publicRoutePatterns.some((p) => p.test(fullText));
|
|
2269
|
+
const inlineAuthDetected = hasInlineAuthCheck(lastArg, fullText);
|
|
2270
|
+
const hasAuth = hasAuthMiddleware || inlineAuthDetected;
|
|
2271
|
+
const isPublic = isPublicRoute(fullText);
|
|
1993
2272
|
if (!hasAuth && !isPublic) {
|
|
1994
|
-
|
|
1995
|
-
if (/\/(?:api|admin|user|account|setting|profile)/i.test(fullText)) {
|
|
1996
|
-
confidence = 0.6;
|
|
1997
|
-
}
|
|
1998
|
-
if (/\.(post|put|patch|delete)\s*\(/i.test(expressionText)) {
|
|
1999
|
-
confidence += 0.1;
|
|
2000
|
-
}
|
|
2273
|
+
const confidence = buildAuthConfidence(expressionText, fullText);
|
|
2001
2274
|
return {
|
|
2002
2275
|
ruleId: "auth/missing-check",
|
|
2003
2276
|
severity: "medium",
|
|
@@ -284,7 +284,7 @@ function validateSqlSchema(content, errors, warnings) {
|
|
|
284
284
|
var riskDetectorLoader = null;
|
|
285
285
|
function loadRiskDetectorModule() {
|
|
286
286
|
if (!riskDetectorLoader) {
|
|
287
|
-
riskDetectorLoader = import('./risk-detector-
|
|
287
|
+
riskDetectorLoader = import('./risk-detector-4U6ZJ2G5.js').then((module) => ({
|
|
288
288
|
detectSchemaRisks: module.detectSchemaRisks
|
|
289
289
|
})).catch((error) => {
|
|
290
290
|
riskDetectorLoader = null;
|