@sentriflow/cli 0.4.1 → 0.5.0
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 +235 -290
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4698,36 +4698,6 @@ var RuleEngine = class {
|
|
|
4698
4698
|
}
|
|
4699
4699
|
};
|
|
4700
4700
|
|
|
4701
|
-
// ../core/src/pack-loader/types.ts
|
|
4702
|
-
var PackLoadError = class extends Error {
|
|
4703
|
-
constructor(code, message, details) {
|
|
4704
|
-
super(message);
|
|
4705
|
-
this.code = code;
|
|
4706
|
-
this.details = details;
|
|
4707
|
-
this.name = "PackLoadError";
|
|
4708
|
-
if (Error.captureStackTrace) {
|
|
4709
|
-
Error.captureStackTrace(this, this.constructor);
|
|
4710
|
-
}
|
|
4711
|
-
}
|
|
4712
|
-
};
|
|
4713
|
-
var GRPX_CONSTANTS = {
|
|
4714
|
-
MAGIC: "GRPX",
|
|
4715
|
-
HEADER_SIZE: 76,
|
|
4716
|
-
CURRENT_VERSION: 1,
|
|
4717
|
-
ALG_AES_256_GCM: 1,
|
|
4718
|
-
KDF_PBKDF2: 1,
|
|
4719
|
-
KDF_ARGON2ID: 2,
|
|
4720
|
-
PBKDF2_ITERATIONS: 1e5,
|
|
4721
|
-
KEY_LENGTH: 32,
|
|
4722
|
-
IV_LENGTH: 12,
|
|
4723
|
-
TAG_LENGTH: 16,
|
|
4724
|
-
SALT_LENGTH: 32
|
|
4725
|
-
};
|
|
4726
|
-
|
|
4727
|
-
// ../core/src/pack-loader/PackLoader.ts
|
|
4728
|
-
import { createDecipheriv, pbkdf2Sync } from "crypto";
|
|
4729
|
-
import { createContext, Script } from "vm";
|
|
4730
|
-
|
|
4731
4701
|
// ../core/src/helpers/index.ts
|
|
4732
4702
|
var helpers_exports = {};
|
|
4733
4703
|
__export(helpers_exports, {
|
|
@@ -10470,110 +10440,6 @@ function buildAllHelpers() {
|
|
|
10470
10440
|
return result;
|
|
10471
10441
|
}
|
|
10472
10442
|
var allHelpers = buildAllHelpers();
|
|
10473
|
-
async function loadEncryptedPack(packData, options) {
|
|
10474
|
-
const { licenseKey, machineId: machineId2, getActivationCount, timeout = 5e3 } = options;
|
|
10475
|
-
if (packData.length < GRPX_CONSTANTS.HEADER_SIZE) {
|
|
10476
|
-
throw new PackLoadError("INVALID_FORMAT", "Pack file too small");
|
|
10477
|
-
}
|
|
10478
|
-
const magic = packData.toString("utf8", 0, 4);
|
|
10479
|
-
if (magic !== GRPX_CONSTANTS.MAGIC) {
|
|
10480
|
-
throw new PackLoadError("INVALID_FORMAT", "Invalid pack format (bad magic)");
|
|
10481
|
-
}
|
|
10482
|
-
const version = packData.readUInt8(4);
|
|
10483
|
-
const algorithm = packData.readUInt8(5);
|
|
10484
|
-
const kdf = packData.readUInt8(6);
|
|
10485
|
-
const iv = packData.subarray(12, 24);
|
|
10486
|
-
const tag = packData.subarray(24, 40);
|
|
10487
|
-
const salt = packData.subarray(40, 72);
|
|
10488
|
-
const payloadLength = packData.readUInt32BE(72);
|
|
10489
|
-
const encryptedPayload = packData.subarray(
|
|
10490
|
-
GRPX_CONSTANTS.HEADER_SIZE,
|
|
10491
|
-
GRPX_CONSTANTS.HEADER_SIZE + payloadLength
|
|
10492
|
-
);
|
|
10493
|
-
if (version !== GRPX_CONSTANTS.CURRENT_VERSION) {
|
|
10494
|
-
throw new PackLoadError("INVALID_FORMAT", `Unsupported version: ${version}`);
|
|
10495
|
-
}
|
|
10496
|
-
if (algorithm !== GRPX_CONSTANTS.ALG_AES_256_GCM) {
|
|
10497
|
-
throw new PackLoadError("INVALID_FORMAT", `Unsupported algorithm: ${algorithm}`);
|
|
10498
|
-
}
|
|
10499
|
-
let key;
|
|
10500
|
-
if (kdf === GRPX_CONSTANTS.KDF_PBKDF2) {
|
|
10501
|
-
key = pbkdf2Sync(
|
|
10502
|
-
licenseKey,
|
|
10503
|
-
salt,
|
|
10504
|
-
GRPX_CONSTANTS.PBKDF2_ITERATIONS,
|
|
10505
|
-
GRPX_CONSTANTS.KEY_LENGTH,
|
|
10506
|
-
"sha256"
|
|
10507
|
-
);
|
|
10508
|
-
} else {
|
|
10509
|
-
throw new PackLoadError("INVALID_FORMAT", `Unsupported KDF: ${kdf}`);
|
|
10510
|
-
}
|
|
10511
|
-
let decryptedSource;
|
|
10512
|
-
try {
|
|
10513
|
-
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
10514
|
-
decipher.setAuthTag(tag);
|
|
10515
|
-
const decrypted = Buffer.concat([
|
|
10516
|
-
decipher.update(encryptedPayload),
|
|
10517
|
-
decipher.final()
|
|
10518
|
-
]);
|
|
10519
|
-
decryptedSource = decrypted.toString("utf8");
|
|
10520
|
-
} catch (error) {
|
|
10521
|
-
const errorType = error instanceof Error ? error.name : "Unknown";
|
|
10522
|
-
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
10523
|
-
if (process.env.DEBUG) {
|
|
10524
|
-
console.error(`[PackLoader] Decryption failed: ${errorType} - ${errorMsg}`);
|
|
10525
|
-
}
|
|
10526
|
-
throw new PackLoadError(
|
|
10527
|
-
"DECRYPTION_FAILED",
|
|
10528
|
-
"Invalid license key or corrupted pack"
|
|
10529
|
-
);
|
|
10530
|
-
}
|
|
10531
|
-
const sandbox = createValidationSandbox({ machineId: machineId2, getActivationCount });
|
|
10532
|
-
const context = createContext(sandbox);
|
|
10533
|
-
let vmResult;
|
|
10534
|
-
try {
|
|
10535
|
-
const script = new Script(decryptedSource, {
|
|
10536
|
-
filename: "pack.js",
|
|
10537
|
-
timeout
|
|
10538
|
-
});
|
|
10539
|
-
const factory = script.runInContext(context);
|
|
10540
|
-
vmResult = factory(sandbox);
|
|
10541
|
-
if (!vmResult || !Array.isArray(vmResult.rules)) {
|
|
10542
|
-
throw new PackLoadError("VALIDATION_FAILED", "Invalid pack structure");
|
|
10543
|
-
}
|
|
10544
|
-
} catch (error) {
|
|
10545
|
-
if (error instanceof PackLoadError) {
|
|
10546
|
-
throw error;
|
|
10547
|
-
}
|
|
10548
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
10549
|
-
if (msg.includes("EXPIRED")) {
|
|
10550
|
-
throw new PackLoadError("EXPIRED", msg);
|
|
10551
|
-
}
|
|
10552
|
-
if (msg.includes("MACHINE_MISMATCH")) {
|
|
10553
|
-
throw new PackLoadError("MACHINE_MISMATCH", msg);
|
|
10554
|
-
}
|
|
10555
|
-
if (msg.includes("ACTIVATION_LIMIT")) {
|
|
10556
|
-
throw new PackLoadError("ACTIVATION_LIMIT", msg);
|
|
10557
|
-
}
|
|
10558
|
-
throw new PackLoadError("VALIDATION_FAILED", msg);
|
|
10559
|
-
}
|
|
10560
|
-
const nativeRules = vmResult.rules.map((ruleDef) => {
|
|
10561
|
-
const checkFn = compileNativeCheckFunction(ruleDef.checkSource);
|
|
10562
|
-
return {
|
|
10563
|
-
id: ruleDef.id,
|
|
10564
|
-
selector: ruleDef.selector,
|
|
10565
|
-
vendor: ruleDef.vendor,
|
|
10566
|
-
metadata: ruleDef.metadata,
|
|
10567
|
-
check: checkFn
|
|
10568
|
-
};
|
|
10569
|
-
});
|
|
10570
|
-
return {
|
|
10571
|
-
metadata: vmResult.metadata,
|
|
10572
|
-
rules: nativeRules,
|
|
10573
|
-
validUntil: vmResult.validUntil,
|
|
10574
|
-
licenseInfo: vmResult.licenseInfo ?? void 0
|
|
10575
|
-
};
|
|
10576
|
-
}
|
|
10577
10443
|
var helperNames = Object.keys(allHelpers).filter(
|
|
10578
10444
|
(key) => typeof allHelpers[key] === "function" || typeof allHelpers[key] === "object"
|
|
10579
10445
|
);
|
|
@@ -10589,68 +10455,6 @@ function compileNativeCheckFunction(source) {
|
|
|
10589
10455
|
const compiledFn = wrapperFn(allHelpers);
|
|
10590
10456
|
return compiledFn;
|
|
10591
10457
|
}
|
|
10592
|
-
function createValidationSandbox(options) {
|
|
10593
|
-
const RealDate = Date;
|
|
10594
|
-
return Object.freeze({
|
|
10595
|
-
// Safe Date access (read-only, can't be mocked)
|
|
10596
|
-
Date: Object.freeze({
|
|
10597
|
-
now: () => RealDate.now(),
|
|
10598
|
-
parse: (s) => RealDate.parse(s)
|
|
10599
|
-
}),
|
|
10600
|
-
// Safe JSON access
|
|
10601
|
-
JSON: Object.freeze({
|
|
10602
|
-
parse: JSON.parse,
|
|
10603
|
-
stringify: JSON.stringify
|
|
10604
|
-
}),
|
|
10605
|
-
// Safe Math access
|
|
10606
|
-
Math: Object.freeze(Math),
|
|
10607
|
-
// No-op console (for debugging in pack factory)
|
|
10608
|
-
console: Object.freeze({
|
|
10609
|
-
log: () => {
|
|
10610
|
-
},
|
|
10611
|
-
warn: () => {
|
|
10612
|
-
},
|
|
10613
|
-
error: () => {
|
|
10614
|
-
}
|
|
10615
|
-
}),
|
|
10616
|
-
// Provided context for validation
|
|
10617
|
-
machineId: options.machineId,
|
|
10618
|
-
getActivationCount: options.getActivationCount,
|
|
10619
|
-
// Basic primitives
|
|
10620
|
-
undefined: void 0,
|
|
10621
|
-
null: null,
|
|
10622
|
-
NaN: NaN,
|
|
10623
|
-
Infinity: Infinity,
|
|
10624
|
-
// Required for error throwing in factory
|
|
10625
|
-
Error
|
|
10626
|
-
});
|
|
10627
|
-
}
|
|
10628
|
-
function validatePackFormat(packData) {
|
|
10629
|
-
if (packData.length < GRPX_CONSTANTS.HEADER_SIZE) {
|
|
10630
|
-
return false;
|
|
10631
|
-
}
|
|
10632
|
-
const magic = packData.toString("utf8", 0, 4);
|
|
10633
|
-
if (magic !== GRPX_CONSTANTS.MAGIC) {
|
|
10634
|
-
return false;
|
|
10635
|
-
}
|
|
10636
|
-
const version = packData.readUInt8(4);
|
|
10637
|
-
if (version !== GRPX_CONSTANTS.CURRENT_VERSION) {
|
|
10638
|
-
return false;
|
|
10639
|
-
}
|
|
10640
|
-
const algorithm = packData.readUInt8(5);
|
|
10641
|
-
if (algorithm !== GRPX_CONSTANTS.ALG_AES_256_GCM) {
|
|
10642
|
-
return false;
|
|
10643
|
-
}
|
|
10644
|
-
const kdf = packData.readUInt8(6);
|
|
10645
|
-
if (kdf !== GRPX_CONSTANTS.KDF_PBKDF2) {
|
|
10646
|
-
return false;
|
|
10647
|
-
}
|
|
10648
|
-
const payloadLength = packData.readUInt32BE(72);
|
|
10649
|
-
if (packData.length < GRPX_CONSTANTS.HEADER_SIZE + payloadLength) {
|
|
10650
|
-
return false;
|
|
10651
|
-
}
|
|
10652
|
-
return true;
|
|
10653
|
-
}
|
|
10654
10458
|
|
|
10655
10459
|
// ../core/src/pack-loader/format-detector.ts
|
|
10656
10460
|
import { open } from "node:fs/promises";
|
|
@@ -10658,12 +10462,10 @@ import { resolve } from "node:path";
|
|
|
10658
10462
|
var FORMAT_PRIORITIES = {
|
|
10659
10463
|
unknown: 0,
|
|
10660
10464
|
unencrypted: 100,
|
|
10661
|
-
grpx: 200,
|
|
10662
10465
|
grx2: 300
|
|
10663
10466
|
};
|
|
10664
10467
|
var MAGIC_BYTES = {
|
|
10665
|
-
GRX2: Buffer.from("GRX2", "ascii")
|
|
10666
|
-
GRPX: Buffer.from("GRPX", "ascii")
|
|
10468
|
+
GRX2: Buffer.from("GRX2", "ascii")
|
|
10667
10469
|
};
|
|
10668
10470
|
var MAGIC_BYTES_LENGTH = 4;
|
|
10669
10471
|
async function detectPackFormat(filePath) {
|
|
@@ -10688,9 +10490,6 @@ async function detectPackFormat(filePath) {
|
|
|
10688
10490
|
if (buffer.equals(MAGIC_BYTES.GRX2)) {
|
|
10689
10491
|
return "grx2";
|
|
10690
10492
|
}
|
|
10691
|
-
if (buffer.equals(MAGIC_BYTES.GRPX)) {
|
|
10692
|
-
return "grpx";
|
|
10693
|
-
}
|
|
10694
10493
|
return "unencrypted";
|
|
10695
10494
|
} catch (error) {
|
|
10696
10495
|
if (error instanceof Error) {
|
|
@@ -10738,9 +10537,9 @@ async function getMachineId() {
|
|
|
10738
10537
|
|
|
10739
10538
|
// ../core/src/grx2-loader/GRX2ExtendedLoader.ts
|
|
10740
10539
|
import {
|
|
10741
|
-
createDecipheriv
|
|
10540
|
+
createDecipheriv,
|
|
10742
10541
|
createHash,
|
|
10743
|
-
pbkdf2Sync
|
|
10542
|
+
pbkdf2Sync,
|
|
10744
10543
|
timingSafeEqual
|
|
10745
10544
|
} from "node:crypto";
|
|
10746
10545
|
import { readFile, readdir } from "node:fs/promises";
|
|
@@ -10756,11 +10555,11 @@ function deriveLDK(licenseKey, ldkSalt, machineId2) {
|
|
|
10756
10555
|
ldkSalt,
|
|
10757
10556
|
Buffer.from(machineId2, "utf-8")
|
|
10758
10557
|
]);
|
|
10759
|
-
return
|
|
10558
|
+
return pbkdf2Sync(licenseKey, combinedSalt, PBKDF2_ITERATIONS, AES_KEY_SIZE, "sha256");
|
|
10760
10559
|
}
|
|
10761
10560
|
function decrypt(ciphertext, key, iv, authTag) {
|
|
10762
10561
|
try {
|
|
10763
|
-
const decipher =
|
|
10562
|
+
const decipher = createDecipheriv(AES_ALGORITHM, key, iv);
|
|
10764
10563
|
decipher.setAuthTag(authTag);
|
|
10765
10564
|
const plaintext = Buffer.concat([
|
|
10766
10565
|
decipher.update(ciphertext),
|
|
@@ -11151,7 +10950,7 @@ function getHelperRegistry() {
|
|
|
11151
10950
|
}
|
|
11152
10951
|
|
|
11153
10952
|
// ../core/src/json-rules/ExpressionEvaluator.ts
|
|
11154
|
-
import { createContext
|
|
10953
|
+
import { createContext, Script } from "vm";
|
|
11155
10954
|
var MAX_EXPR_LENGTH = 1e3;
|
|
11156
10955
|
var EXPR_TIMEOUT_MS = 50;
|
|
11157
10956
|
var BLOCKED_PATTERNS = [
|
|
@@ -11271,7 +11070,7 @@ var ExpressionEvaluator = class {
|
|
|
11271
11070
|
sandboxObj[namespace] = Object.freeze({ ...vendorHelpers });
|
|
11272
11071
|
}
|
|
11273
11072
|
}
|
|
11274
|
-
return
|
|
11073
|
+
return createContext(Object.freeze(sandboxObj));
|
|
11275
11074
|
}
|
|
11276
11075
|
/**
|
|
11277
11076
|
* Pre-compile an expression for later evaluation.
|
|
@@ -11288,7 +11087,7 @@ var ExpressionEvaluator = class {
|
|
|
11288
11087
|
return true;
|
|
11289
11088
|
}
|
|
11290
11089
|
try {
|
|
11291
|
-
const script = new
|
|
11090
|
+
const script = new Script(`(${expr})`, {
|
|
11292
11091
|
filename: "expr.js"
|
|
11293
11092
|
});
|
|
11294
11093
|
scriptCache.set(expr, script);
|
|
@@ -11311,7 +11110,7 @@ var ExpressionEvaluator = class {
|
|
|
11311
11110
|
let script = scriptCache.get(expr);
|
|
11312
11111
|
if (!script) {
|
|
11313
11112
|
try {
|
|
11314
|
-
script = new
|
|
11113
|
+
script = new Script(`(${expr})`, {
|
|
11315
11114
|
filename: "expr.js"
|
|
11316
11115
|
});
|
|
11317
11116
|
scriptCache.set(expr, script);
|
|
@@ -11319,7 +11118,7 @@ var ExpressionEvaluator = class {
|
|
|
11319
11118
|
return false;
|
|
11320
11119
|
}
|
|
11321
11120
|
}
|
|
11322
|
-
const evalContext =
|
|
11121
|
+
const evalContext = createContext({
|
|
11323
11122
|
...this.sandbox,
|
|
11324
11123
|
node: createSafeNode(node)
|
|
11325
11124
|
});
|
|
@@ -12524,7 +12323,7 @@ function generateSarif(results, filePath, rules, options = {}, ipSummary) {
|
|
|
12524
12323
|
tool: {
|
|
12525
12324
|
driver: {
|
|
12526
12325
|
name: "Sentriflow",
|
|
12527
|
-
version: "0.
|
|
12326
|
+
version: "0.5.0",
|
|
12528
12327
|
informationUri: "https://github.com/sentriflow/sentriflow",
|
|
12529
12328
|
rules: sarifRules,
|
|
12530
12329
|
// SEC-007: Include CWE taxonomy when rules reference it
|
|
@@ -12684,7 +12483,7 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
|
12684
12483
|
tool: {
|
|
12685
12484
|
driver: {
|
|
12686
12485
|
name: "Sentriflow",
|
|
12687
|
-
version: "0.
|
|
12486
|
+
version: "0.5.0",
|
|
12688
12487
|
informationUri: "https://github.com/sentriflow/sentriflow",
|
|
12689
12488
|
rules: sarifRules,
|
|
12690
12489
|
// SEC-007: Include CWE taxonomy when rules reference it
|
|
@@ -12728,6 +12527,103 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
|
12728
12527
|
return JSON.stringify(report, null, 2);
|
|
12729
12528
|
}
|
|
12730
12529
|
|
|
12530
|
+
// src/human.ts
|
|
12531
|
+
var COLORS = {
|
|
12532
|
+
red: "\x1B[31m",
|
|
12533
|
+
yellow: "\x1B[33m",
|
|
12534
|
+
blue: "\x1B[34m",
|
|
12535
|
+
gray: "\x1B[90m",
|
|
12536
|
+
bold: "\x1B[1m",
|
|
12537
|
+
reset: "\x1B[0m"
|
|
12538
|
+
};
|
|
12539
|
+
var SEVERITY_COLORS = {
|
|
12540
|
+
error: COLORS.red,
|
|
12541
|
+
warning: COLORS.yellow,
|
|
12542
|
+
info: COLORS.blue
|
|
12543
|
+
};
|
|
12544
|
+
function countSeverities(results) {
|
|
12545
|
+
const counts = { error: 0, warning: 0, info: 0, total: 0 };
|
|
12546
|
+
for (const r of results) {
|
|
12547
|
+
if (!r.passed) {
|
|
12548
|
+
if (r.level === "error" || r.level === "warning" || r.level === "info") {
|
|
12549
|
+
counts[r.level]++;
|
|
12550
|
+
}
|
|
12551
|
+
counts.total++;
|
|
12552
|
+
}
|
|
12553
|
+
}
|
|
12554
|
+
return counts;
|
|
12555
|
+
}
|
|
12556
|
+
function formatFinding(result, color) {
|
|
12557
|
+
const line = result.loc?.startLine ?? 0;
|
|
12558
|
+
const col = 1;
|
|
12559
|
+
const location = `${line}:${col}`.padEnd(8);
|
|
12560
|
+
const severityColor = color ? SEVERITY_COLORS[result.level] ?? "" : "";
|
|
12561
|
+
const reset = color ? COLORS.reset : "";
|
|
12562
|
+
const severityText = result.level.padEnd(7);
|
|
12563
|
+
const severity = `${severityColor}${severityText}${reset}`;
|
|
12564
|
+
const ruleColor = color ? COLORS.gray : "";
|
|
12565
|
+
const ruleId = `${ruleColor}${result.ruleId}${reset}`;
|
|
12566
|
+
return ` ${location} ${severity} ${result.message} ${ruleId}`;
|
|
12567
|
+
}
|
|
12568
|
+
function formatSummary(counts, color) {
|
|
12569
|
+
if (counts.total === 0) {
|
|
12570
|
+
const check = color ? `${COLORS.bold}\u2714${COLORS.reset}` : "\u2714";
|
|
12571
|
+
return `
|
|
12572
|
+
${check} No problems found
|
|
12573
|
+
`;
|
|
12574
|
+
}
|
|
12575
|
+
const x = color ? `${COLORS.red}\u2716${COLORS.reset}` : "\u2716";
|
|
12576
|
+
const parts = [];
|
|
12577
|
+
if (counts.error > 0) {
|
|
12578
|
+
parts.push(`${counts.error} error${counts.error !== 1 ? "s" : ""}`);
|
|
12579
|
+
}
|
|
12580
|
+
if (counts.warning > 0) {
|
|
12581
|
+
parts.push(`${counts.warning} warning${counts.warning !== 1 ? "s" : ""}`);
|
|
12582
|
+
}
|
|
12583
|
+
if (counts.info > 0) {
|
|
12584
|
+
parts.push(`${counts.info} info`);
|
|
12585
|
+
}
|
|
12586
|
+
const problemText = counts.total === 1 ? "problem" : "problems";
|
|
12587
|
+
return `
|
|
12588
|
+
${x} ${counts.total} ${problemText} (${parts.join(", ")})
|
|
12589
|
+
`;
|
|
12590
|
+
}
|
|
12591
|
+
function formatHuman(results, filePath, options) {
|
|
12592
|
+
const color = options?.color ?? false;
|
|
12593
|
+
const failures = results.filter((r) => !r.passed);
|
|
12594
|
+
const lines = [];
|
|
12595
|
+
const header = color ? `${COLORS.bold}${filePath}${COLORS.reset}` : filePath;
|
|
12596
|
+
lines.push(header);
|
|
12597
|
+
for (const result of failures) {
|
|
12598
|
+
lines.push(formatFinding(result, color));
|
|
12599
|
+
}
|
|
12600
|
+
const counts = countSeverities(results);
|
|
12601
|
+
lines.push(formatSummary(counts, color));
|
|
12602
|
+
return lines.join("\n");
|
|
12603
|
+
}
|
|
12604
|
+
function formatMultiFileHuman(files, options) {
|
|
12605
|
+
const color = options?.color ?? false;
|
|
12606
|
+
const lines = [];
|
|
12607
|
+
const totalCounts = { error: 0, warning: 0, info: 0, total: 0 };
|
|
12608
|
+
for (const { file, results } of files) {
|
|
12609
|
+
const failures = results.filter((r) => !r.passed);
|
|
12610
|
+
if (failures.length === 0) continue;
|
|
12611
|
+
const header = color ? `${COLORS.bold}${file}${COLORS.reset}` : file;
|
|
12612
|
+
lines.push(header);
|
|
12613
|
+
for (const result of failures) {
|
|
12614
|
+
lines.push(formatFinding(result, color));
|
|
12615
|
+
}
|
|
12616
|
+
lines.push("");
|
|
12617
|
+
const counts = countSeverities(results);
|
|
12618
|
+
totalCounts.error += counts.error;
|
|
12619
|
+
totalCounts.warning += counts.warning;
|
|
12620
|
+
totalCounts.info += counts.info;
|
|
12621
|
+
totalCounts.total += counts.total;
|
|
12622
|
+
}
|
|
12623
|
+
lines.push(formatSummary(totalCounts, color));
|
|
12624
|
+
return lines.join("\n");
|
|
12625
|
+
}
|
|
12626
|
+
|
|
12731
12627
|
// ../rules-default/src/common/network-rules.ts
|
|
12732
12628
|
var NoMulticastBroadcastIp = {
|
|
12733
12629
|
id: "NET-IP-001",
|
|
@@ -15767,6 +15663,101 @@ async function createPackDescriptors(packPaths) {
|
|
|
15767
15663
|
return descriptors;
|
|
15768
15664
|
}
|
|
15769
15665
|
|
|
15666
|
+
// src/loaders/pack-loader.ts
|
|
15667
|
+
async function loadGRX2Pack(options) {
|
|
15668
|
+
const { filePath, licenseKey, machineId: machineId2, cacheDir } = options;
|
|
15669
|
+
try {
|
|
15670
|
+
const rulePack = await loadExtendedPack(filePath, licenseKey, machineId2);
|
|
15671
|
+
return {
|
|
15672
|
+
rulePack,
|
|
15673
|
+
usedLicensingLoader: false
|
|
15674
|
+
};
|
|
15675
|
+
} catch (coreError) {
|
|
15676
|
+
const errorCode = coreError?.code;
|
|
15677
|
+
if (errorCode !== "NOT_EXTENDED_FORMAT") {
|
|
15678
|
+
throw coreError;
|
|
15679
|
+
}
|
|
15680
|
+
try {
|
|
15681
|
+
const licensingModulePath = "@sentriflow/licensing";
|
|
15682
|
+
const licensing = await import(
|
|
15683
|
+
/* @vite-ignore */
|
|
15684
|
+
licensingModulePath
|
|
15685
|
+
);
|
|
15686
|
+
const effectiveCacheDir = cacheDir ?? licensing.getDefaultCacheDir();
|
|
15687
|
+
const cacheManager = licensing.createCacheManager(licenseKey, machineId2, effectiveCacheDir);
|
|
15688
|
+
let tierTMK;
|
|
15689
|
+
let tierTMKs;
|
|
15690
|
+
try {
|
|
15691
|
+
const cachedTMK = await cacheManager.getTierTMK();
|
|
15692
|
+
if (cachedTMK) {
|
|
15693
|
+
tierTMK = Buffer.from(cachedTMK.tmk, "base64");
|
|
15694
|
+
}
|
|
15695
|
+
const allTMKs = await cacheManager.tmkCache.getAllTierTMKs();
|
|
15696
|
+
if (allTMKs.size > 0) {
|
|
15697
|
+
tierTMKs = /* @__PURE__ */ new Map();
|
|
15698
|
+
for (const [tierId, tmkData] of allTMKs) {
|
|
15699
|
+
tierTMKs.set(tierId, Buffer.from(tmkData.tmk, "base64"));
|
|
15700
|
+
}
|
|
15701
|
+
}
|
|
15702
|
+
} catch (cacheError) {
|
|
15703
|
+
if (process.env.DEBUG) {
|
|
15704
|
+
console.debug(
|
|
15705
|
+
"[pack-loader] TMK cache retrieval skipped:",
|
|
15706
|
+
cacheError instanceof Error ? cacheError.message : "Unknown error"
|
|
15707
|
+
);
|
|
15708
|
+
}
|
|
15709
|
+
}
|
|
15710
|
+
const loader = new licensing.GRX2Loader({
|
|
15711
|
+
formatPolicy: "auto",
|
|
15712
|
+
licenseKey,
|
|
15713
|
+
machineId: machineId2,
|
|
15714
|
+
tierTMK,
|
|
15715
|
+
tierTMKs
|
|
15716
|
+
});
|
|
15717
|
+
const result = await loader.loadFromFile(filePath);
|
|
15718
|
+
return {
|
|
15719
|
+
rulePack: result.rulePack,
|
|
15720
|
+
usedLicensingLoader: true
|
|
15721
|
+
};
|
|
15722
|
+
} catch (licensingError) {
|
|
15723
|
+
if (isModuleNotFoundError(licensingError)) {
|
|
15724
|
+
throw new Error('Pack requires v2 format support - run "sentriflow activate" first');
|
|
15725
|
+
}
|
|
15726
|
+
throw licensingError;
|
|
15727
|
+
}
|
|
15728
|
+
}
|
|
15729
|
+
}
|
|
15730
|
+
function isModuleNotFoundError(error) {
|
|
15731
|
+
return error instanceof Error && (error.message.includes("Cannot find module") || error.message.includes("Cannot find package") || error.code === "ERR_MODULE_NOT_FOUND");
|
|
15732
|
+
}
|
|
15733
|
+
function sanitizeErrorMessage(msg) {
|
|
15734
|
+
const sanitized = msg.replace(/(?:\/[\w.-]+)+\/([\w.-]+)/g, "$1");
|
|
15735
|
+
return sanitized.replace(/\s+at\s+.+/g, "").trim();
|
|
15736
|
+
}
|
|
15737
|
+
function mapGRX2LoadError(error) {
|
|
15738
|
+
if (error instanceof Error && "code" in error) {
|
|
15739
|
+
const code = error.code;
|
|
15740
|
+
const messages = {
|
|
15741
|
+
// GRX2LoaderError codes (from licensing)
|
|
15742
|
+
HEADER_INVALID: "Pack file is corrupted or invalid",
|
|
15743
|
+
DECRYPTION_FAILED: "Failed to decrypt pack (invalid key or corrupted data)",
|
|
15744
|
+
PARSE_ERROR: "Pack content is malformed",
|
|
15745
|
+
FILE_READ_ERROR: "Cannot read pack file",
|
|
15746
|
+
HASH_MISMATCH: "Pack integrity check failed",
|
|
15747
|
+
TMK_NOT_AVAILABLE: 'License not activated - run "sentriflow activate" first',
|
|
15748
|
+
// EncryptedPackError codes (from core)
|
|
15749
|
+
LICENSE_MISSING: "Invalid or missing license key",
|
|
15750
|
+
LICENSE_EXPIRED: "License has expired",
|
|
15751
|
+
LICENSE_INVALID: "License key is invalid for this pack",
|
|
15752
|
+
MACHINE_MISMATCH: "License is not valid for this machine",
|
|
15753
|
+
PACK_CORRUPTED: "Pack file is corrupted or invalid",
|
|
15754
|
+
NOT_EXTENDED_FORMAT: "Pack requires extended format (v3) - activate license for v2 pack support"
|
|
15755
|
+
};
|
|
15756
|
+
return messages[code] ?? "Pack load failed";
|
|
15757
|
+
}
|
|
15758
|
+
return error instanceof Error ? sanitizeErrorMessage(error.message) : "Pack load failed";
|
|
15759
|
+
}
|
|
15760
|
+
|
|
15770
15761
|
// src/loaders/index.ts
|
|
15771
15762
|
function validatePathOrThrow(path, pathValidator, errorContext, baseDirs) {
|
|
15772
15763
|
const validation = pathValidator(path, baseDirs);
|
|
@@ -16083,44 +16074,6 @@ async function loadRulePackFile(packPath, baseDirs) {
|
|
|
16083
16074
|
errorContext: "rule pack"
|
|
16084
16075
|
});
|
|
16085
16076
|
}
|
|
16086
|
-
function mapPackLoadError(error) {
|
|
16087
|
-
const messages = {
|
|
16088
|
-
DECRYPTION_FAILED: "Invalid license key for encrypted pack",
|
|
16089
|
-
EXPIRED: "Encrypted pack has expired",
|
|
16090
|
-
MACHINE_MISMATCH: "License is not valid for this machine",
|
|
16091
|
-
ACTIVATION_LIMIT: "Maximum activations exceeded for this license"
|
|
16092
|
-
};
|
|
16093
|
-
throw new SentriflowConfigError(
|
|
16094
|
-
messages[error.code] ?? `Failed to load encrypted pack: ${error.message}`
|
|
16095
|
-
);
|
|
16096
|
-
}
|
|
16097
|
-
async function loadEncryptedRulePack(packPath, licenseKey, baseDirs) {
|
|
16098
|
-
const canonicalPath = validatePathOrThrow(
|
|
16099
|
-
packPath,
|
|
16100
|
-
validatePackPath,
|
|
16101
|
-
"encrypted pack",
|
|
16102
|
-
baseDirs
|
|
16103
|
-
);
|
|
16104
|
-
try {
|
|
16105
|
-
const packData = await readFileAsync(canonicalPath);
|
|
16106
|
-
if (!validatePackFormat(packData)) {
|
|
16107
|
-
throw new SentriflowConfigError("Invalid encrypted pack format");
|
|
16108
|
-
}
|
|
16109
|
-
const loadedPack = await loadEncryptedPack(packData, {
|
|
16110
|
-
licenseKey,
|
|
16111
|
-
timeout: 1e4
|
|
16112
|
-
});
|
|
16113
|
-
return {
|
|
16114
|
-
...loadedPack.metadata,
|
|
16115
|
-
priority: 200,
|
|
16116
|
-
rules: loadedPack.rules
|
|
16117
|
-
};
|
|
16118
|
-
} catch (error) {
|
|
16119
|
-
if (error instanceof PackLoadError) mapPackLoadError(error);
|
|
16120
|
-
if (error instanceof SentriflowConfigError) throw error;
|
|
16121
|
-
throw new SentriflowConfigError("Failed to load encrypted rule pack");
|
|
16122
|
-
}
|
|
16123
|
-
}
|
|
16124
16077
|
async function resolveRules(options = {}) {
|
|
16125
16078
|
const {
|
|
16126
16079
|
configPath,
|
|
@@ -16226,9 +16179,7 @@ async function resolveRules(options = {}) {
|
|
|
16226
16179
|
console.error(`Warning: Pack detection failed: ${errorMsg}`);
|
|
16227
16180
|
packDescriptors = [];
|
|
16228
16181
|
}
|
|
16229
|
-
const hasEncryptedPacks = packDescriptors.some(
|
|
16230
|
-
(d) => d.format === "grx2" || d.format === "grpx"
|
|
16231
|
-
);
|
|
16182
|
+
const hasEncryptedPacks = packDescriptors.some((d) => d.format === "grx2");
|
|
16232
16183
|
if (hasEncryptedPacks && !licenseKey) {
|
|
16233
16184
|
const errorMsg = "License key required for encrypted packs (use --license-key or set SENTRIFLOW_LICENSE_KEY)";
|
|
16234
16185
|
if (strictPacks) {
|
|
@@ -16269,24 +16220,12 @@ async function resolveRules(options = {}) {
|
|
|
16269
16220
|
console.error(`Warning: Skipping GRX2 pack (no machine ID): ${desc.path}`);
|
|
16270
16221
|
continue;
|
|
16271
16222
|
}
|
|
16272
|
-
|
|
16273
|
-
validation.canonicalPath,
|
|
16223
|
+
const result = await loadGRX2Pack({
|
|
16224
|
+
filePath: validation.canonicalPath,
|
|
16274
16225
|
licenseKey,
|
|
16275
|
-
machineId2
|
|
16276
|
-
);
|
|
16277
|
-
loadedPack
|
|
16278
|
-
break;
|
|
16279
|
-
}
|
|
16280
|
-
case "grpx": {
|
|
16281
|
-
if (!licenseKey) {
|
|
16282
|
-
console.error(`Warning: Skipping GRPX pack (no license key): ${desc.path}`);
|
|
16283
|
-
continue;
|
|
16284
|
-
}
|
|
16285
|
-
loadedPack = await loadEncryptedRulePack(
|
|
16286
|
-
validation.canonicalPath,
|
|
16287
|
-
licenseKey,
|
|
16288
|
-
allowedBaseDirs
|
|
16289
|
-
);
|
|
16226
|
+
machineId: machineId2
|
|
16227
|
+
});
|
|
16228
|
+
loadedPack = result.rulePack;
|
|
16290
16229
|
loadedPack.priority = desc.priority;
|
|
16291
16230
|
break;
|
|
16292
16231
|
}
|
|
@@ -16306,21 +16245,7 @@ async function resolveRules(options = {}) {
|
|
|
16306
16245
|
loadedCount++;
|
|
16307
16246
|
totalRules += loadedPack.rules.length;
|
|
16308
16247
|
} catch (error) {
|
|
16309
|
-
|
|
16310
|
-
if (error instanceof EncryptedPackError) {
|
|
16311
|
-
const messages = {
|
|
16312
|
-
LICENSE_MISSING: "Invalid or missing license key",
|
|
16313
|
-
LICENSE_EXPIRED: "License has expired",
|
|
16314
|
-
LICENSE_INVALID: "License key is invalid for this pack",
|
|
16315
|
-
DECRYPTION_FAILED: "Failed to decrypt pack (invalid key or corrupted data)",
|
|
16316
|
-
MACHINE_MISMATCH: "License is not valid for this machine",
|
|
16317
|
-
PACK_CORRUPTED: "Pack file is corrupted or invalid",
|
|
16318
|
-
NOT_EXTENDED_FORMAT: "Pack is not in extended GRX2 format"
|
|
16319
|
-
};
|
|
16320
|
-
errorMsg = messages[error.code] ?? `Pack load error: ${error.message}`;
|
|
16321
|
-
} else {
|
|
16322
|
-
errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
16323
|
-
}
|
|
16248
|
+
const errorMsg = mapGRX2LoadError(error);
|
|
16324
16249
|
if (strictPacks) {
|
|
16325
16250
|
throw new SentriflowConfigError(
|
|
16326
16251
|
`Failed to load pack '${desc.path}': ${errorMsg}`
|
|
@@ -16682,9 +16607,9 @@ function enrichResultsWithRuleMetadata(results, rules) {
|
|
|
16682
16607
|
});
|
|
16683
16608
|
}
|
|
16684
16609
|
var program = new Command();
|
|
16685
|
-
program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.
|
|
16610
|
+
program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.5.0").argument("[files...]", "Path(s) to configuration file(s) (supports multiple files)").option("--ast", "Output the AST instead of rule results").option("-f, --format <format>", "Output format (json, human, sarif)", "json").option("-q, --quiet", "Only output failures (suppress passed results)").option("-c, --config <path>", "Path to config file (default: auto-detect)").option("--no-config", "Ignore config file").option("-r, --rules <path>", "Additional rules file to load (legacy)").option(
|
|
16686
16611
|
"--pack <path...>",
|
|
16687
|
-
"Path(s) to rule pack(s) (
|
|
16612
|
+
"Path(s) to rule pack(s) (.grx2 encrypted or unencrypted JS/TS modules)"
|
|
16688
16613
|
).option(
|
|
16689
16614
|
"--license-key <key>",
|
|
16690
16615
|
"License key for encrypted rule packs (or set SENTRIFLOW_LICENSE_KEY)"
|
|
@@ -17109,6 +17034,13 @@ Parsing complete: ${allAsts.length} files`);
|
|
|
17109
17034
|
console.log(
|
|
17110
17035
|
generateMultiFileSarif(allFileResults, rules, sarifOptions)
|
|
17111
17036
|
);
|
|
17037
|
+
} else if (options.format === "human") {
|
|
17038
|
+
const isColorEnabled = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
17039
|
+
const humanFiles = allFileResults.map((fr) => ({
|
|
17040
|
+
file: fr.filePath,
|
|
17041
|
+
results: fr.results
|
|
17042
|
+
}));
|
|
17043
|
+
console.log(formatMultiFileHuman(humanFiles, { color: isColorEnabled }));
|
|
17112
17044
|
} else {
|
|
17113
17045
|
const output = {
|
|
17114
17046
|
summary: {
|
|
@@ -17226,6 +17158,9 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
17226
17158
|
baseDir: process.cwd()
|
|
17227
17159
|
};
|
|
17228
17160
|
console.log(generateSarif(results2, "<stdin>", stdinRules, sarifOptions, stdinIpSummary));
|
|
17161
|
+
} else if (options.format === "human") {
|
|
17162
|
+
const isColorEnabled = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
17163
|
+
console.log(formatHuman(results2, "<stdin>", { color: isColorEnabled }));
|
|
17229
17164
|
} else {
|
|
17230
17165
|
const output = {
|
|
17231
17166
|
file: "<stdin>",
|
|
@@ -17325,6 +17260,13 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
17325
17260
|
console.log(
|
|
17326
17261
|
generateMultiFileSarif(allFileResults, rules, sarifOptions)
|
|
17327
17262
|
);
|
|
17263
|
+
} else if (options.format === "human") {
|
|
17264
|
+
const isColorEnabled = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
17265
|
+
const humanFiles = allFileResults.map((fr) => ({
|
|
17266
|
+
file: fr.filePath,
|
|
17267
|
+
results: fr.results
|
|
17268
|
+
}));
|
|
17269
|
+
console.log(formatMultiFileHuman(humanFiles, { color: isColorEnabled }));
|
|
17328
17270
|
} else {
|
|
17329
17271
|
const output = {
|
|
17330
17272
|
summary: {
|
|
@@ -17380,7 +17322,7 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
17380
17322
|
} else {
|
|
17381
17323
|
try {
|
|
17382
17324
|
vendor = getVendor(options.vendor);
|
|
17383
|
-
} catch
|
|
17325
|
+
} catch {
|
|
17384
17326
|
console.error(`Error: Unknown vendor '${options.vendor}'`);
|
|
17385
17327
|
console.error(
|
|
17386
17328
|
`Available vendors: ${getAvailableVendors().join(", ")}, auto`
|
|
@@ -17443,6 +17385,9 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
17443
17385
|
console.log(
|
|
17444
17386
|
generateSarif(results, filePath, singleFileRules, sarifOptions, ipSummary)
|
|
17445
17387
|
);
|
|
17388
|
+
} else if (options.format === "human") {
|
|
17389
|
+
const isColorEnabled = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
17390
|
+
console.log(formatHuman(results, filePath, { color: isColorEnabled }));
|
|
17446
17391
|
} else {
|
|
17447
17392
|
const output = {
|
|
17448
17393
|
vendor: {
|
|
@@ -17477,7 +17422,7 @@ async function loadLicensingExtension() {
|
|
|
17477
17422
|
licensingModulePath
|
|
17478
17423
|
);
|
|
17479
17424
|
if (licensing.registerCommands) {
|
|
17480
|
-
licensing.registerCommands(program);
|
|
17425
|
+
licensing.registerCommands(program, { cliVersion: "0.5.0" });
|
|
17481
17426
|
}
|
|
17482
17427
|
} catch {
|
|
17483
17428
|
const licensingMessage = `
|