korext 0.9.3 → 0.9.5

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 (3) hide show
  1. package/README.md +11 -1
  2. package/bin/korext.js +70 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -99,6 +99,16 @@ OWASP Top 10 | PCI-DSS | HIPAA | GDPR | SOC 2 | NIST SP 800-53 | NIST SP 800-171
99
99
  | `KOREXT_API_TOKEN` | API authentication token (recommended) |
100
100
  | `KOREXT_TOKEN` | Deprecated alias (shows warning) |
101
101
 
102
+ ## Changelog
103
+
104
+ ### v0.9.5
105
+
106
+ Fixed
107
+ - Watch mode now detects file changes correctly and scans on startup
108
+ - Enforcing a nonexistent directory now prints an error and exits with code 2 instead of silently passing
109
+ - Offline enforcement prints how many rules are available versus how many require server analysis
110
+ - Policy commands now default to the production API instead of localhost
111
+
102
112
  ## Links
103
113
 
104
114
  - Website: [korext.com](https://www.korext.com)
@@ -112,4 +122,4 @@ OWASP Top 10 | PCI-DSS | HIPAA | GDPR | SOC 2 | NIST SP 800-53 | NIST SP 800-171
112
122
 
113
123
  **Publisher**: Korext
114
124
  **License**: Proprietary
115
- **Version**: 0.8.0
125
+ **Version**: 0.9.5
package/bin/korext.js CHANGED
@@ -172,8 +172,11 @@ program
172
172
  .action((tokenArg) => {
173
173
  let token = tokenArg;
174
174
  if (!token) {
175
- console.log(`\nPlease generate an API token at ${chalk.cyan('https://app.korext.com/settings/tokens')}`);
176
- console.log(`Then run: ${chalk.green('korext login <your-token>')}\n`);
175
+ console.log(`\nTo authenticate, sign in at ${chalk.cyan('https://app.korext.com')} and use your session token.`);
176
+ console.log(`Long-lived API tokens for CI/CD are coming soon.`);
177
+ console.log(`\nFor CI/CD, Korext works in anonymous mode (20 requests per hour).`);
178
+ console.log(`Set ${chalk.green('KOREXT_API_TOKEN')} for higher limits.`);
179
+ console.log(`\nThen run: ${chalk.green('korext login <your-token>')}\n`);
177
180
  process.exit(1);
178
181
  }
179
182
  const config = getConfig();
@@ -264,6 +267,14 @@ program
264
267
  let forceOffline = options.offline;
265
268
  let localDefinitions = null;
266
269
 
270
+ // Pre-flight: verify directory exists
271
+ const resolvedInputDir = path.resolve(dir);
272
+ if (!fs.existsSync(resolvedInputDir)) {
273
+ if (isText) console.error(chalk.red(`\n✖ Directory does not exist: ${resolvedInputDir}`));
274
+ else console.error(JSON.stringify({ error: `Directory does not exist: ${resolvedInputDir}` }));
275
+ process.exit(2);
276
+ }
277
+
267
278
  if (isText) {
268
279
  console.log(`\n${chalk.bold.hex('#F27D26')('▲ KOREXT CLI ENFORCEMENT ENGINE')} v${version}`);
269
280
  console.log(chalk.dim('======================================='));
@@ -292,6 +303,11 @@ program
292
303
  if (isText) {
293
304
  console.log(chalk.yellow('\n⚡ Offline mode: using local rule engine (regex-based analysis)'));
294
305
  console.log(chalk.dim(` Cached rules: v${localDefinitions.version} · ${localDefinitions.ruleCount} rules · ${localDefinitions.packCount} packs`));
306
+ // Show rule coverage breakdown
307
+ const packRules = localDefinitions.packs?.[pack]?.rules || [];
308
+ const availableCount = packRules.filter(r => localDefinitions.rules?.[r]).length;
309
+ const serverOnlyCount = packRules.length - availableCount;
310
+ console.log(chalk.dim(` Offline mode: ${availableCount} of ${packRules.length} rules available. ${serverOnlyCount} rules require server analysis.`));
295
311
  console.log(chalk.dim(' Note: AI-powered deep analysis is unavailable in offline mode.\n'));
296
312
  }
297
313
  }
@@ -331,9 +347,12 @@ program
331
347
 
332
348
  let usedLocalEngine = false;
333
349
 
350
+ const resolvedDir = path.resolve(dir);
351
+
334
352
  for (let i = 0; i < files.length; i++) {
335
353
  const file = files[i];
336
354
  const displayPath = path.relative(process.cwd(), file);
355
+ const sarifPath = path.relative(resolvedDir, file);
337
356
 
338
357
  let fileSpinner = null;
339
358
  if (isText) fileSpinner = ora(`Analyzing ${displayPath} (${i + 1}/${files.length})...`).start();
@@ -433,39 +452,51 @@ program
433
452
  }
434
453
  storedV.push(v);
435
454
  }
436
- report.results.push({ file: displayPath, violations: storedV });
455
+ report.results.push({ file: displayPath, sarifFile: sarifPath, violations: storedV });
437
456
  } else {
438
457
  if (fileSpinner) fileSpinner.stop();
439
- report.results.push({ file: displayPath, violations: [] });
458
+ report.results.push({ file: displayPath, sarifFile: sarifPath, violations: [] });
440
459
  }
441
460
  }
442
461
 
443
462
  if (format === 'json') {
444
463
  console.log(JSON.stringify(report, null, 2));
445
464
  } else if (format === 'sarif') {
446
- const sarif = {
447
- version: "2.1.0",
448
- $schema: "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
449
- runs: [{
450
- tool: { driver: { name: "Korext", version } },
451
- results: []
452
- }]
453
- };
465
+ // Build SARIF results
466
+ const sarifResults = [];
454
467
  for (const res of report.results) {
455
468
  for (const v of res.violations) {
456
- sarif.runs[0].results.push({
457
- ruleId: v.ruleName,
469
+ const messageText = v.explanation
470
+ || (v.ruleName ? v.ruleName + ': ' + (v.message || 'violation detected') : (v.ruleId || 'unknown') + ' violation detected');
471
+ sarifResults.push({
472
+ ruleId: v.ruleName || v.ruleId || 'unknown',
458
473
  level: v.severity === 'critical' || v.severity === 'high' ? "error" : v.severity === 'medium' ? "warning" : "note",
459
- message: { text: v.explanation },
474
+ message: { text: messageText },
460
475
  locations: [{
461
476
  physicalLocation: {
462
- artifactLocation: { uri: res.file },
477
+ artifactLocation: { uri: res.sarifFile || res.file },
463
478
  region: { startLine: v.line, startColumn: v.column || 1 }
464
479
  }
465
480
  }]
466
481
  });
467
482
  }
468
483
  }
484
+
485
+ // Build driver.rules from unique ruleIds
486
+ const ruleIds = [...new Set(sarifResults.map(r => r.ruleId))];
487
+ const rules = ruleIds.map(id => ({
488
+ id,
489
+ shortDescription: { text: id.replace(/-/g, ' ') }
490
+ }));
491
+
492
+ const sarif = {
493
+ version: "2.1.0",
494
+ $schema: "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
495
+ runs: [{
496
+ tool: { driver: { name: "Korext", version, rules } },
497
+ results: sarifResults
498
+ }]
499
+ };
469
500
  console.log(JSON.stringify(sarif, null, 2));
470
501
  } else {
471
502
  console.log('\n' + chalk.dim('======================================='));
@@ -1291,6 +1322,29 @@ program
1291
1322
  if (fs.existsSync(fullPath)) analyzeWatchedFile(fullPath);
1292
1323
  }, DEBOUNCE_MS));
1293
1324
  });
1325
+
1326
+ // Also do an initial scan of all existing files on startup
1327
+ const initialFiles = [];
1328
+ function walkDir(d) {
1329
+ let entries;
1330
+ try { entries = fs.readdirSync(d, { withFileTypes: true }); } catch { return; }
1331
+ for (const entry of entries) {
1332
+ if (IGNORE_DIRS.has(entry.name)) continue;
1333
+ const fullPath = path.join(d, entry.name);
1334
+ if (entry.isDirectory()) {
1335
+ walkDir(fullPath);
1336
+ } else if (SUPPORTED_EXTS.has(path.extname(entry.name))) {
1337
+ initialFiles.push(fullPath);
1338
+ }
1339
+ }
1340
+ }
1341
+ walkDir(dir);
1342
+ if (initialFiles.length > 0) {
1343
+ console.log(chalk.dim(` Initial scan: ${initialFiles.length} file(s)...\n`));
1344
+ for (const f of initialFiles) {
1345
+ await analyzeWatchedFile(f);
1346
+ }
1347
+ }
1294
1348
  } catch (e) {
1295
1349
  console.error(chalk.red(`Failed to watch ${dir}: ${e.message}`));
1296
1350
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "korext",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Korext Command Line Interface",
5
5
  "type": "module",
6
6
  "main": "bin/korext.js",