@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.
Files changed (81) hide show
  1. package/dist/{build-BXUJKYHC.js → build-HUDIP6KU.js} +153 -164
  2. package/dist/{cache-H63JKFYH.js → cache-N7WNPEYF.js} +2 -3
  3. package/dist/check-LOMVIRHX.js +12 -0
  4. package/dist/{chunk-HPYJPB5Y.js → chunk-2APB25TT.js} +44 -10
  5. package/dist/chunk-3WDV32GA.js +33 -0
  6. package/dist/chunk-5FT3F36G.js +59 -0
  7. package/dist/{chunk-7QV7U6NI.js → chunk-6FAU4IGR.js} +2 -1
  8. package/dist/{chunk-CE3DEYFT.js → chunk-7B5C6U2K.js} +2 -208
  9. package/dist/{chunk-GOGRLQNP.js → chunk-AFY3TX4I.js} +1 -1
  10. package/dist/{chunk-KWX3JHCY.js → chunk-AKZAN4BC.js} +6 -1
  11. package/dist/{chunk-XJBQINSA.js → chunk-CCW3PLQY.js} +2 -2
  12. package/dist/{chunk-IBVVGH6X.js → chunk-EMB6IZFT.js} +17 -4
  13. package/dist/chunk-FHG3ILE4.js +2011 -0
  14. package/dist/{chunk-22CS6EMA.js → chunk-H2AHNI75.js} +1 -1
  15. package/dist/{chunk-UU55OH7P.js → chunk-KE6QJBZG.js} +2 -3
  16. package/dist/{check-6AB5NGWK.js → chunk-QM53IQHM.js} +14 -12
  17. package/dist/{chunk-RRGQCUKT.js → chunk-WJXC4MVY.js} +30 -3
  18. package/dist/chunk-XDCHRVE3.js +215 -0
  19. package/dist/{chunk-P7U52PBY.js → chunk-Z4Z5DNW4.js} +49 -2
  20. package/dist/{ci-V3PIG2GI.js → ci-XY6IKEDC.js} +1938 -238
  21. package/dist/cli/contract-output.d.ts +1 -0
  22. package/dist/{cli-GFRZCJQR.js → cli-UZA4RBNQ.js} +216 -173
  23. package/dist/commands/build/actors/validate.d.ts +2 -0
  24. package/dist/commands/check/commands/check.d.ts +8 -3
  25. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +12 -6
  26. package/dist/commands/ci/machine/actors/db/production-preview.d.ts +10 -0
  27. package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +77 -0
  28. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +11 -0
  29. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +9 -1
  30. package/dist/commands/ci/machine/commands/machine-runner.d.ts +2 -0
  31. package/dist/commands/ci/machine/formatters/sections/production-schema-status.d.ts +30 -0
  32. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +3 -3
  33. package/dist/commands/ci/machine/helpers.d.ts +8 -0
  34. package/dist/commands/ci/machine/machine.d.ts +57 -4
  35. package/dist/commands/ci/machine/types.d.ts +2 -0
  36. package/dist/commands/ci/utils/execa-helpers.d.ts +1 -0
  37. package/dist/commands/db/commands/db-sync/error-classifier.d.ts +9 -0
  38. package/dist/commands/dev/actors/index.d.ts +5 -0
  39. package/dist/commands/dev/actors/tables-manifest.d.ts +16 -0
  40. package/dist/commands/dev/contract.d.ts +1 -1
  41. package/dist/commands/dev/guards.d.ts +24 -0
  42. package/dist/commands/dev/machine.d.ts +22 -3
  43. package/dist/commands/dev/types.d.ts +2 -0
  44. package/dist/commands/doctor.d.ts +9 -0
  45. package/dist/commands/inject-test-attrs/defaults.d.ts +9 -0
  46. package/dist/commands/template-check/commands/template-check.d.ts +1 -0
  47. package/dist/commands/template-check/contract.d.ts +1 -0
  48. package/dist/commands/utils/machine-state-logging.d.ts +20 -0
  49. package/dist/commands/utils/repo-root.d.ts +2 -0
  50. package/dist/constants/versions.d.ts +1 -1
  51. package/dist/{db-HR7CREX2.js → db-Q3GF7JWP.js} +518 -2234
  52. package/dist/{dev-A7RW6XQV.js → dev-5YXNPTCJ.js} +168 -49
  53. package/dist/doctor-MZLOA53G.js +44 -0
  54. package/dist/{env-B47Z4747.js → env-GMB3THRG.js} +6 -7
  55. package/dist/{env-files-K2C7O7L5.js → env-files-2UIUYLLR.js} +2 -2
  56. package/dist/{error-handler-4EYSDOSE.js → error-handler-HEXBRNVV.js} +2 -2
  57. package/dist/{hotfix-CULKKMGS.js → hotfix-NDTPY2T4.js} +4 -4
  58. package/dist/index.js +4 -4
  59. package/dist/{init-ELK5QCWR.js → init-U4VCRHTD.js} +5 -6
  60. package/dist/{inject-test-attrs-Y5UD5P7Q.js → inject-test-attrs-P44BVTQS.js} +5 -18
  61. package/dist/{link-C43JRZWY.js → link-VSNDVZZD.js} +2 -3
  62. package/dist/manifest-TMFLESHW.js +19 -0
  63. package/dist/{risk-detector-BXUY2WKS.js → risk-detector-4U6ZJ2G5.js} +1 -1
  64. package/dist/{risk-detector-core-O7I7SPR7.js → risk-detector-core-TK4OAI3N.js} +2 -2
  65. package/dist/{risk-detector-plpgsql-SGMVKYJP.js → risk-detector-plpgsql-HWKS4OLR.js} +37 -7
  66. package/dist/{status-IJ4ZWHMX.js → status-UTKS63AB.js} +2 -3
  67. package/dist/{telemetry-FN7V727Y.js → telemetry-P56UBLZ2.js} +2 -3
  68. package/dist/{template-check-PNG5NQ5H.js → template-check-FFJVDLBF.js} +63 -35
  69. package/dist/{test-QYXE5UVW.js → test-V4KQL574.js} +34 -10
  70. package/dist/{test-gen-QPWOIEHU.js → test-gen-FS4CEY3P.js} +2 -3
  71. package/dist/{upgrade-3SLWVNAC.js → upgrade-7TWORWBV.js} +18 -6
  72. package/dist/{validate-SM4PXPS7.js → validate-CAAW4Y44.js} +2 -3
  73. package/dist/{vuln-check-TYQNEFS7.js → vuln-check-6CMNPSBR.js} +3 -4
  74. package/dist/{vuln-checker-2QXGN5YT.js → vuln-checker-EJJTNDNE.js} +413 -140
  75. package/dist/{watch-UCDVOQAH.js → watch-PNTKZYFB.js} +1 -1
  76. package/dist/{workflow-ZB5Q2PFY.js → workflow-H75N4BXX.js} +3 -4
  77. package/package.json +2 -2
  78. package/dist/chunk-JT5SUTWE.js +0 -9
  79. package/dist/chunk-M47WJJVS.js +0 -71
  80. package/dist/manifest-2NOQ2IMK.js +0 -32
  81. 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-GOGRLQNP.js';
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(createPermissiveFinding);
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 lineFindings = scanLineForSecrets(lines[i], i + 1, filePath);
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
- const sqlMethods = ["query", "execute", "raw", "unsafe", "exec", "sql", "runQuery"];
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 argText = firstArg.getText();
1752
- const safePatterns = [
1753
- /^sql`/,
1754
- // Tagged template literal (Drizzle)
1755
- /^sql\.raw\(sql`/,
1756
- // sql.raw(sql`...`) is parameterized
1757
- /\$\d+/,
1758
- // Positional parameters ($1, $2)
1759
- /\?/,
1760
- // Question mark parameters
1761
- /:[\w]+/
1762
- // Named parameters (:name)
1763
- ];
1764
- if (safePatterns.some((p) => p.test(argText))) {
1765
- return null;
1766
- }
1767
- let isDangerous = false;
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
- const routeMethods = [
1932
- "get",
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 authMiddlewarePatterns = [
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 handlerText = lastArg?.getText() || "";
1970
- const hasInlineAuthCheck = /headers\.authorization/i.test(handlerText) || /getToken\s*\(/i.test(handlerText) || /session\./i.test(handlerText) || /req\.user\b/i.test(handlerText) || /ctx\.(?:user|auth|session)\b/i.test(handlerText) || /throw.*(?:Unauthorized|401)/i.test(handlerText);
1971
- const hasAuthInFullText = authMiddlewarePatterns.some((p) => p.test(fullText));
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
- let confidence = 0.4;
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-BXUY2WKS.js').then((module) => ({
287
+ riskDetectorLoader = import('./risk-detector-4U6ZJ2G5.js').then((module) => ({
288
288
  detectSchemaRisks: module.detectSchemaRisks
289
289
  })).catch((error) => {
290
290
  riskDetectorLoader = null;