kodingo-cli 1.1.7 → 1.1.9

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.
@@ -41,16 +41,13 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  return result;
42
42
  };
43
43
  })();
44
- var __importDefault = (this && this.__importDefault) || function (mod) {
45
- return (mod && mod.__esModule) ? mod : { "default": mod };
46
- };
47
44
  Object.defineProperty(exports, "__esModule", { value: true });
48
45
  exports.registerInitCommand = registerInitCommand;
49
46
  const readline = __importStar(require("node:readline"));
50
47
  const fs = __importStar(require("fs"));
51
48
  const path = __importStar(require("path"));
52
49
  const os = __importStar(require("os"));
53
- const open_1 = __importDefault(require("open"));
50
+ // open is imported dynamically to avoid ESM/CJS compatibility issues on Windows
54
51
  const persistence_config_1 = require("../utils/persistence-config");
55
52
  const API_BASE = "https://kodingo-api.onrender.com";
56
53
  const APP_URL = "https://kodingo.xyz";
@@ -140,7 +137,8 @@ async function browserLogin(rl) {
140
137
  });
141
138
  // Open browser
142
139
  try {
143
- await (0, open_1.default)(loginUrl);
140
+ const { default: open } = await Promise.resolve().then(() => __importStar(require("open")));
141
+ await open(loginUrl);
144
142
  }
145
143
  catch { }
146
144
  console.log("Waiting for login...");
@@ -130,7 +130,15 @@ async function securityScanCommand(options = {}) {
130
130
  }
131
131
  }
132
132
  catch { }
133
- const candidates = (0, security_code_analyzer_1.analyzeForSecurityCandidates)(repoRoot, graph, symbols, dynamicRules);
133
+ const allCandidates = (0, security_code_analyzer_1.analyzeForSecurityCandidates)(repoRoot, graph, symbols, dynamicRules);
134
+ // Cap at 30 candidates for LLM judgment — prioritise critical and high severity
135
+ const SEVERITY_ORDER = { critical: 0, high: 1, moderate: 2, low: 3 };
136
+ const candidates = allCandidates
137
+ .sort((a, b) => (SEVERITY_ORDER[a.severity ?? 'low'] ?? 3) - (SEVERITY_ORDER[b.severity ?? 'low'] ?? 3))
138
+ .slice(0, 30);
139
+ if (!options.silent && allCandidates.length > 30) {
140
+ console.log(` Found ${allCandidates.length} candidates — reviewing top 30 by severity\n`);
141
+ }
134
142
  if (candidates.length === 0) {
135
143
  if (!options.silent)
136
144
  console.log(" ✔ No suspicious patterns detected.");
@@ -14,10 +14,16 @@
14
14
  * server for Layer 2 — an LLM judgment with full visibility, which is what
15
15
  * actually adjudicates real risk vs false positive.
16
16
  */
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
17
20
  Object.defineProperty(exports, "__esModule", { value: true });
18
21
  exports.findMissingAuthCandidatesLocal = findMissingAuthCandidatesLocal;
19
22
  exports.analyzeForSecurityCandidates = analyzeForSecurityCandidates;
20
23
  const context_assembler_1 = require("./context-assembler");
24
+ const fs_1 = __importDefault(require("fs"));
25
+ const path_1 = __importDefault(require("path"));
26
+ const child_process_1 = require("child_process");
21
27
  const code_pattern_scanner_1 = require("./code-pattern-scanner");
22
28
  const ROUTE_REGISTRATION_METHOD_NAMES = new Set(["get", "post", "put", "delete", "patch", "addhook", "all"]);
23
29
  const AUTH_FUNCTION_HINTS = [
@@ -80,6 +86,77 @@ function analyzeForSecurityCandidates(repoRoot, graph, symbols, dynamicRules = [
80
86
  const candidates = [];
81
87
  // ── Code pattern findings — static + dynamic rules ────────────────────────
82
88
  const patternFindings = (0, code_pattern_scanner_1.scanAllSymbolsForPatterns)(symbols);
89
+ // ── Tier 1: Fast file-level scan (no LLM, runs on all files) ─────────────
90
+ // Catches vulnerabilities in procedural code outside named functions.
91
+ // Only high-confidence patterns are included — no ambiguous matches.
92
+ // File selection is smart: recently changed files first, then unscanned,
93
+ // capped at 100 files per run to stay fast and avoid timeouts.
94
+ const HIGH_CONFIDENCE_PATTERNS = new Set(["sql_injection", "command_injection", "hardcoded_secret", "path_traversal"]);
95
+ const SUPPORTED_EXTS = new Set([".php", ".py", ".go", ".rs", ".java", ".kt", ".rb", ".c", ".cpp", ".html", ".phtml", ".js", ".ts", ".jsx", ".tsx"]);
96
+ const EXT_TO_LANG = {
97
+ ".php": "php", ".phtml": "php", ".py": "python", ".go": "go", ".rs": "rust",
98
+ ".java": "java", ".kt": "kotlin", ".rb": "ruby", ".c": "c", ".cpp": "cpp",
99
+ ".html": "html", ".js": "javascript", ".ts": "typescript", ".jsx": "javascript", ".tsx": "typescript"
100
+ };
101
+ const IGNORED_DIRS = new Set(["node_modules", ".git", "dist", "build", "vendor", "__pycache__", ".next", "out", "coverage"]);
102
+ // Get recently changed files from git (last 30 days) — scan these first
103
+ let priorityFiles = [];
104
+ try {
105
+ const gitOut = (0, child_process_1.execSync)("git log --since=30.days --name-only --pretty=format: --diff-filter=AM", { cwd: repoRoot, timeout: 5000 }).toString();
106
+ priorityFiles = gitOut.split("\n").filter(f => f.trim() && SUPPORTED_EXTS.has(path_1.default.extname(f)))
107
+ .map(f => path_1.default.join(repoRoot, f)).filter(f => fs_1.default.existsSync(f)).slice(0, 50);
108
+ }
109
+ catch { }
110
+ // Walk repo for remaining files — skip already-in-priority
111
+ const prioritySet = new Set(priorityFiles);
112
+ const allWalked = [];
113
+ const walk = (dir) => {
114
+ try {
115
+ for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
116
+ const full = path_1.default.join(dir, entry.name);
117
+ if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name))
118
+ walk(full);
119
+ else if (entry.isFile() && SUPPORTED_EXTS.has(path_1.default.extname(entry.name)) && !prioritySet.has(full)) {
120
+ allWalked.push(full);
121
+ }
122
+ }
123
+ }
124
+ catch { }
125
+ };
126
+ walk(repoRoot);
127
+ // Priority files first, then remaining up to cap of 100 total
128
+ const filesToScan = [...priorityFiles, ...allWalked].slice(0, 100);
129
+ const alreadyScannedFiles = new Set(symbols.map(s => s.file));
130
+ for (const filePath of filesToScan) {
131
+ if (alreadyScannedFiles.has(filePath))
132
+ continue; // already covered by symbol scan
133
+ try {
134
+ const source = fs_1.default.readFileSync(filePath, "utf-8");
135
+ const ext = path_1.default.extname(filePath);
136
+ const language = EXT_TO_LANG[ext] ?? "javascript";
137
+ const syntheticSymbol = {
138
+ name: path_1.default.basename(filePath),
139
+ file: filePath,
140
+ startLine: 1,
141
+ body: source.slice(0, 10000),
142
+ fingerprint: `file:${filePath}`,
143
+ calls: [],
144
+ routes: [],
145
+ imports: [],
146
+ language,
147
+ type: "function",
148
+ endLine: source.split("\n").length,
149
+ };
150
+ const fileFindings = (0, code_pattern_scanner_1.scanSymbolForPatterns)(syntheticSymbol, language)
151
+ .filter(f => HIGH_CONFIDENCE_PATTERNS.has(f.patternType)); // Tier 1: high-confidence only
152
+ for (const f of fileFindings) {
153
+ patternFindings.push(f);
154
+ }
155
+ }
156
+ catch {
157
+ continue;
158
+ }
159
+ }
83
160
  // Apply dynamic rules generated from OSV vulnerability data
84
161
  for (const sym of symbols) {
85
162
  const language = sym.language ?? "unknown";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodingo-cli",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Kodingo CLI",
5
5
  "license": "MIT",
6
6
  "private": false,