@oculum/cli 1.0.14 → 1.0.15

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 (2) hide show
  1. package/dist/index.js +81 -33
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -12404,6 +12404,29 @@ var require_entropy = __commonJS({
12404
12404
  ];
12405
12405
  return debugPatterns.some((pattern) => pattern.test(lineContent));
12406
12406
  }
12407
+ function isCommandLineSnippet(value, lineContent) {
12408
+ const commandContextPatterns = [
12409
+ /\b(quickFix|command|example|usage|cli|help|hint)\b/i,
12410
+ /\b(run|exec|execute|install)\b\s*:/i
12411
+ ];
12412
+ const envAssignmentPatterns = [
12413
+ /^\s*(export\s+)?[A-Z_][A-Z0-9_]*=/,
12414
+ // export VAR=... or VAR=...
12415
+ /\bNODE_OPTIONS=/i
12416
+ ];
12417
+ const commandPatterns = [
12418
+ /^\s*\$\s+/,
12419
+ // shell prompt
12420
+ /\s--[a-z0-9][a-z0-9-]*/i,
12421
+ // CLI flags
12422
+ /\b(npm|pnpm|yarn|npx|node|bun|deno|git|curl|wget|oculum|python|pip|brew)\b/i
12423
+ ];
12424
+ const hasCommandContext = commandContextPatterns.some((p2) => p2.test(lineContent));
12425
+ const looksLikeEnvAssignment = envAssignmentPatterns.some((p2) => p2.test(value));
12426
+ const looksLikeCommand = commandPatterns.some((p2) => p2.test(value));
12427
+ const hasMultipleTokens = value.trim().split(/\s+/).length >= 2;
12428
+ return (hasCommandContext || looksLikeEnvAssignment || looksLikeCommand) && hasMultipleTokens;
12429
+ }
12407
12430
  function isInlineStyle(lineContent) {
12408
12431
  const jsxStylePatterns = [
12409
12432
  /style\s*=\s*\{\{/,
@@ -12556,6 +12579,8 @@ var require_entropy = __commonJS({
12556
12579
  continue;
12557
12580
  if (isDebugLogContent(lineContent))
12558
12581
  continue;
12582
+ if (isCommandLineSnippet(value, lineContent))
12583
+ continue;
12559
12584
  if (isRegexPattern(value))
12560
12585
  continue;
12561
12586
  if (isTemplateWithCode(value, lineContent))
@@ -14317,7 +14342,11 @@ var require_path_exclusions = __commonJS({
14317
14342
  "**/demo/**",
14318
14343
  "**/sample/**",
14319
14344
  "**/samples/**",
14320
- "**/playground/**"
14345
+ "**/playground/**",
14346
+ "**/oculum.json",
14347
+ // Scanner output files
14348
+ "**/*.scan.json"
14349
+ // Any scan result files
14321
14350
  ],
14322
14351
  fixturePatterns: [
14323
14352
  "**/fixtures/**",
@@ -15892,8 +15921,7 @@ var require_dangerous_functions = __commonJS({
15892
15921
  const contextEnd = Math.min(lines.length, lineNumber + 5);
15893
15922
  const context = lines.slice(contextStart, contextEnd).join("\n");
15894
15923
  const cosmeticContextPatterns = [
15895
- // UI component files
15896
- /\/(components?|ui|widgets?|animations?|contexts?)\//i,
15924
+ // UI component files - REMOVED, let severity classification handle these
15897
15925
  // Style-related variables/functions
15898
15926
  /\b(style|styles|css|className|animation|transition)/i,
15899
15927
  /\b(width|height|opacity|color|transform|rotate|scale|translate)/i,
@@ -15906,13 +15934,9 @@ var require_dangerous_functions = __commonJS({
15906
15934
  /delay.*Math\.random/i,
15907
15935
  /duration.*Math\.random/i,
15908
15936
  // UI state variations
15909
- /\b(variant|theme|layout|position).*Math\.random/i,
15910
- // UI identifier variable names (toast, notification, element, component IDs)
15911
- /\b(toast|notification|element|component|widget|modal|dialog|popup).*id\b/i,
15912
- /\bid\s*=.*Math\.random/i,
15913
- /\bkey\s*=.*Math\.random/i,
15914
- // React keys
15915
- /\btempId|temporaryId|uniqueId\b/i
15937
+ /\b(variant|theme|layout|position).*Math\.random/i
15938
+ // NOTE: Removed UI identifier patterns (key, id, tempId, etc.) - these should be
15939
+ // classified with info/low severity by the severity classification logic, not skipped entirely
15916
15940
  ];
15917
15941
  if (cosmeticContextPatterns.some((p2) => p2.test(context))) {
15918
15942
  return true;
@@ -15936,10 +15960,14 @@ var require_dangerous_functions = __commonJS({
15936
15960
  }
15937
15961
  function extractFunctionContext(content, lineNumber) {
15938
15962
  const lines = content.split("\n");
15939
- const start = Math.max(0, lineNumber - 10);
15963
+ const start = Math.max(0, lineNumber - 20);
15940
15964
  for (let i = lineNumber; i >= start; i--) {
15941
15965
  const line = lines[i];
15942
- const funcDeclMatch = line.match(/(?:export\s+)?function\s+(\w+)/i);
15966
+ const hasMethodCallWithArrowCallback = /\.\w+\(.*\([^)]*\)\s*=>/.test(line);
15967
+ if (hasMethodCallWithArrowCallback) {
15968
+ continue;
15969
+ }
15970
+ const funcDeclMatch = line.match(/(?:export\s+)?(?:async\s+)?function\s+(\w+)/i);
15943
15971
  if (funcDeclMatch) {
15944
15972
  return funcDeclMatch[1].toLowerCase();
15945
15973
  }
@@ -16077,7 +16105,24 @@ var require_dangerous_functions = __commonJS({
16077
16105
  "dialog",
16078
16106
  "popup",
16079
16107
  "unique",
16080
- "react"
16108
+ "react",
16109
+ // Non-security randomness usage (backoff/sampling/experiments)
16110
+ "jitter",
16111
+ "retry",
16112
+ "backoff",
16113
+ "delay",
16114
+ "timeout",
16115
+ "latency",
16116
+ "sample",
16117
+ "sampling",
16118
+ "probability",
16119
+ "chance",
16120
+ "rollout",
16121
+ "experiment",
16122
+ "abtest",
16123
+ "cohort",
16124
+ "bucket",
16125
+ "variant"
16081
16126
  ];
16082
16127
  if (lowRiskPatterns.some((p2) => lower.includes(p2))) {
16083
16128
  return "low";
@@ -16109,7 +16154,9 @@ var require_dangerous_functions = __commonJS({
16109
16154
  const inUIContext = isCosmeticMathRandom(lineContent, content, lineNumber);
16110
16155
  const businessLogicPatterns = [
16111
16156
  /\b(business|order|invoice|customer|product|transaction)Id\b/i,
16112
- /\b(reference|tracking|confirmation)Number\b/i
16157
+ /\b(reference|tracking|confirmation)Number\b/i,
16158
+ /\b(backoff|retry|jitter|delay|timeout|latency)\b/i,
16159
+ /\b(sample|sampling|probability|chance|rollout|experiment|abtest|cohort|bucket|variant)\b/i
16113
16160
  ];
16114
16161
  const inBusinessLogicContext = businessLogicPatterns.some((p2) => p2.test(context)) && !inSecurityContext;
16115
16162
  let contextDescription = "unknown context";
@@ -16120,7 +16167,7 @@ var require_dangerous_functions = __commonJS({
16120
16167
  } else if (inUIContext) {
16121
16168
  contextDescription = "UI/cosmetic usage";
16122
16169
  } else if (inBusinessLogicContext) {
16123
- contextDescription = "business identifier generation";
16170
+ contextDescription = "non-security usage";
16124
16171
  }
16125
16172
  return {
16126
16173
  inSecurityContext,
@@ -16388,24 +16435,24 @@ var require_dangerous_functions = __commonJS({
16388
16435
  explanation = " (seed/demo data generation)";
16389
16436
  description = "Math.random() used for generating fixture/seed data. Not security-critical in development contexts.";
16390
16437
  suggestedFix = "Acceptable for seed data. Use crypto.randomUUID() if uniqueness guarantees needed.";
16391
- } else if (nameRisk === "high" || context.inSecurityContext || functionIntent === "security") {
16392
- severity2 = "high";
16393
- confidence2 = "high";
16394
- explanation = " (security-sensitive context)";
16395
- description = "Math.random() is NOT cryptographically secure and MUST NOT be used for tokens, keys, passwords, or session IDs. This can lead to predictable values that attackers can exploit.";
16396
- suggestedFix = "Replace with crypto.randomBytes() or crypto.randomUUID() for security-sensitive operations";
16397
16438
  } else if (toStringPattern.intent === "short-ui-id") {
16398
16439
  severity2 = "info";
16399
16440
  confidence2 = "low";
16400
16441
  explanation = " (UI correlation ID)";
16401
16442
  description = "Math.random() used for short UI correlation IDs. Not security-critical, but collisions possible in high-volume scenarios.";
16402
16443
  suggestedFix = "For UI correlation, crypto.randomUUID() provides better uniqueness guarantees";
16444
+ } else if (nameRisk === "high" || context.inSecurityContext || functionIntent === "security") {
16445
+ severity2 = "high";
16446
+ confidence2 = "high";
16447
+ explanation = " (security-sensitive context)";
16448
+ description = "Math.random() is NOT cryptographically secure and MUST NOT be used for tokens, keys, passwords, or session IDs. This can lead to predictable values that attackers can exploit.";
16449
+ suggestedFix = "Replace with crypto.randomBytes() or crypto.randomUUID() for security-sensitive operations";
16403
16450
  } else if (nameRisk === "low" || context.inBusinessLogicContext || toStringPattern.intent === "business-id") {
16404
16451
  severity2 = "low";
16405
16452
  confidence2 = "low";
16406
- explanation = " (business identifier)";
16407
- description = "Math.random() is being used for non-security purposes (business IDs, tracking numbers). While not critical, Math.random() can produce collisions in high-volume scenarios.";
16408
- suggestedFix = "Consider crypto.randomUUID() for better uniqueness guarantees and collision resistance";
16453
+ explanation = " (non-security usage)";
16454
+ description = "Math.random() is being used for non-security purposes (business IDs, sampling, jitter/backoff, experiments). While not critical, Math.random() can produce collisions or bias in high-volume scenarios.";
16455
+ suggestedFix = "Use crypto.randomUUID() for uniqueness-sensitive IDs. For sampling/backoff, consider a seeded PRNG if determinism is needed.";
16409
16456
  } else {
16410
16457
  severity2 = "medium";
16411
16458
  confidence2 = "medium";
@@ -37475,7 +37522,7 @@ Example response format (OPTIMIZED):
37475
37522
  keep: { type: "boolean" }
37476
37523
  },
37477
37524
  required: ["index", "keep"],
37478
- additionalProperties: true
37525
+ additionalProperties: false
37479
37526
  }
37480
37527
  }
37481
37528
  },
@@ -44649,18 +44696,19 @@ async function status() {
44649
44696
  spinner.succeed(" Authenticated");
44650
44697
  console.log("");
44651
44698
  const email = result.email || config.email || "unknown";
44652
- const tier = result.tier || "free";
44653
- if (tier !== config.tier || email !== config.email) {
44654
- setAuthCredentials(config.apiKey, email, tier);
44699
+ const rawTier = String(result.tier || "free");
44700
+ const normalizedTier = rawTier === "enterprise" ? "max" : rawTier;
44701
+ if (normalizedTier !== config.tier || email !== config.email) {
44702
+ setAuthCredentials(config.apiKey, email, normalizedTier);
44655
44703
  }
44656
- const tierBadge = tier === "pro" ? source_default.bgBlue.white(" PRO ") : tier === "enterprise" ? source_default.bgMagenta.white(" ENTERPRISE ") : source_default.bgGray.white(" FREE ");
44704
+ const tierBadge = normalizedTier === "pro" ? source_default.bgBlue.white(" PRO ") : normalizedTier === "max" ? source_default.bgMagenta.white(" MAX ") : normalizedTier === "starter" ? source_default.bgCyan.white(" STARTER ") : source_default.bgGray.white(" FREE ");
44657
44705
  console.log(source_default.dim(" Email: ") + source_default.white(email));
44658
44706
  console.log(source_default.dim(" Plan: ") + tierBadge);
44659
44707
  console.log("");
44660
44708
  console.log(source_default.bold(" Available Scan Depths:"));
44661
44709
  console.log("");
44662
44710
  console.log(source_default.green(" \u2713 ") + source_default.white("cheap") + source_default.dim(" Fast pattern matching (always free)"));
44663
- if (tier === "pro" || tier === "enterprise") {
44711
+ if (normalizedTier === "pro" || normalizedTier === "max") {
44664
44712
  console.log(source_default.green(" \u2713 ") + source_default.white("validated") + source_default.dim(" AI validation (~70% fewer false positives)"));
44665
44713
  console.log(source_default.dim(" \u{1F512} ") + source_default.white("deep") + source_default.dim(" Multi-agent analysis (coming soon)"));
44666
44714
  } else {
@@ -48559,7 +48607,7 @@ async function runUsageFlow() {
48559
48607
  console.log(source_default.bold(" \u{1F4CA} Oculum Usage"));
48560
48608
  console.log(source_default.dim(" " + "\u2500".repeat(38)));
48561
48609
  console.log("");
48562
- const planBadge = plan.name === "pro" ? source_default.bgBlue.white(" PRO ") : plan.name === "enterprise" || plan.name === "max" ? source_default.bgMagenta.white(" MAX ") : plan.name === "starter" ? source_default.bgCyan.white(" STARTER ") : source_default.bgGray.white(" FREE ");
48610
+ const planBadge = plan.name === "pro" ? source_default.bgBlue.white(" PRO ") : plan.name === "max" ? source_default.bgMagenta.white(" MAX ") : plan.name === "starter" ? source_default.bgCyan.white(" STARTER ") : source_default.bgGray.white(" FREE ");
48563
48611
  console.log(source_default.dim(" Plan: ") + planBadge + source_default.white(` ${plan.displayName}`));
48564
48612
  console.log(source_default.dim(" Month: ") + source_default.white(usageData.month));
48565
48613
  console.log("");
@@ -48851,7 +48899,7 @@ async function usage(options) {
48851
48899
  console.log(source_default.bold(" \u{1F4CA} Oculum Usage"));
48852
48900
  console.log(source_default.dim(" " + "\u2500".repeat(38)));
48853
48901
  console.log("");
48854
- const planBadge = plan.name === "pro" ? source_default.bgBlue.white(" PRO ") : plan.name === "enterprise" || plan.name === "max" ? source_default.bgMagenta.white(" MAX ") : plan.name === "starter" ? source_default.bgCyan.white(" STARTER ") : source_default.bgGray.white(" FREE ");
48902
+ const planBadge = plan.name === "pro" ? source_default.bgBlue.white(" PRO ") : plan.name === "max" ? source_default.bgMagenta.white(" MAX ") : plan.name === "starter" ? source_default.bgCyan.white(" STARTER ") : source_default.bgGray.white(" FREE ");
48855
48903
  console.log(source_default.dim(" Plan: ") + planBadge + source_default.white(` ${plan.displayName}`));
48856
48904
  console.log(source_default.dim(" Month: ") + source_default.white(usageData.month));
48857
48905
  console.log("");
@@ -49350,7 +49398,7 @@ function shouldRunUI() {
49350
49398
  return isOcAlias || isUICommand;
49351
49399
  }
49352
49400
  var program2 = new Command();
49353
- program2.name("oculum").description("AI-native security scanner for detecting vulnerabilities in LLM-generated code").version("1.0.9").addHelpText("after", `
49401
+ program2.name("oculum").description("AI-native security scanner for detecting vulnerabilities in LLM-generated code").version("1.0.15").addHelpText("after", `
49354
49402
  Quick Start:
49355
49403
  $ oculum scan . Scan current directory (free)
49356
49404
  $ oculum ui Interactive mode with guided setup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oculum/cli",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "AI-native security scanner CLI for detecting vulnerabilities in AI-generated code, BYOK patterns, and modern web applications",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -19,14 +19,14 @@
19
19
  "url": "https://github.com/flexipie/oculum/issues"
20
20
  },
21
21
  "scripts": {
22
- "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --banner:js=\"#!/usr/bin/env node\" --define:process.env.OCULUM_API_URL='undefined' --define:VERSION='\"1.0.9\"'",
22
+ "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --banner:js=\"#!/usr/bin/env node\" --define:process.env.OCULUM_API_URL='undefined' --define:VERSION='\"'$npm_package_version'\"'",
23
23
  "dev": "npm run build -- --watch",
24
24
  "test": "echo \"No tests configured yet\"",
25
25
  "lint": "eslint src/"
26
26
  },
27
27
  "dependencies": {
28
- "@oculum/scanner": "^1.0.3",
29
- "@oculum/shared": "^1.0.2",
28
+ "@oculum/scanner": "^1.0.5",
29
+ "@oculum/shared": "^1.0.5",
30
30
  "commander": "^12.1.0",
31
31
  "chalk": "^5.3.0",
32
32
  "ora": "^8.1.1",