@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.
- package/dist/index.js +81 -33
- 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
|
|
15911
|
-
|
|
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 -
|
|
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
|
|
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 = "
|
|
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 = " (
|
|
16407
|
-
description = "Math.random() is being used for non-security purposes (business IDs,
|
|
16408
|
-
suggestedFix = "
|
|
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:
|
|
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
|
|
44653
|
-
|
|
44654
|
-
|
|
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 =
|
|
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 (
|
|
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 === "
|
|
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 === "
|
|
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.
|
|
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.
|
|
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='\"
|
|
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.
|
|
29
|
-
"@oculum/shared": "^1.0.
|
|
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",
|