@pretense/cli 0.6.20 → 0.6.22
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/{chunk-N2GZIZAD.js → chunk-BWS6UMKL.js} +1 -1
- package/dist/index.js +240 -107
- package/dist/postinstall.js +1 -1
- package/package.json +4 -4
|
@@ -17,7 +17,7 @@ function versionFromPackageJson() {
|
|
|
17
17
|
return void 0;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
var PRETENSE_VERSION = (true ? "0.6.
|
|
20
|
+
var PRETENSE_VERSION = (true ? "0.6.22" : void 0) ?? versionFromPackageJson() ?? "0.0.0-unknown";
|
|
21
21
|
|
|
22
22
|
export {
|
|
23
23
|
PRETENSE_VERSION
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
PRETENSE_VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BWS6UMKL.js";
|
|
5
5
|
import {
|
|
6
6
|
__commonJS,
|
|
7
7
|
__toESM,
|
|
@@ -14472,14 +14472,16 @@ async function scanWithPresidio(text, presidioUrl, opts = {}) {
|
|
|
14472
14472
|
|
|
14473
14473
|
// ../packages/mutator/dist/index.js
|
|
14474
14474
|
init_esm_shims();
|
|
14475
|
+
import { existsSync as existsSync4, readFileSync as readFileSync32 } from "fs";
|
|
14476
|
+
import { join as join4 } from "path";
|
|
14475
14477
|
import { createHmac, hkdfSync, randomBytes as randomBytes2 } from "crypto";
|
|
14476
14478
|
import { createHash, randomBytes } from "crypto";
|
|
14477
14479
|
import { readFileSync as readFileSync4, existsSync as existsSync22 } from "fs";
|
|
14478
14480
|
import { homedir as homedir2 } from "os";
|
|
14479
14481
|
import { join as join22, dirname, parse, resolve } from "path";
|
|
14480
|
-
import { chmodSync, existsSync as
|
|
14482
|
+
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync2, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
14481
14483
|
import { homedir } from "os";
|
|
14482
|
-
import { join as
|
|
14484
|
+
import { join as join5 } from "path";
|
|
14483
14485
|
var DIR_MODE = 448;
|
|
14484
14486
|
var FILE_MODE = 384;
|
|
14485
14487
|
var POSIX_MODES_ENFORCED = process.platform !== "win32";
|
|
@@ -14501,10 +14503,10 @@ var PretenseHomeError = class extends Error {
|
|
|
14501
14503
|
}
|
|
14502
14504
|
};
|
|
14503
14505
|
function pretenseHome() {
|
|
14504
|
-
return
|
|
14506
|
+
return join5(homedir(), ".pretense");
|
|
14505
14507
|
}
|
|
14506
14508
|
function ensureDirOwnerOnly(dir) {
|
|
14507
|
-
const preExisting =
|
|
14509
|
+
const preExisting = existsSync5(dir);
|
|
14508
14510
|
if (!preExisting) {
|
|
14509
14511
|
try {
|
|
14510
14512
|
mkdirSync2(dir, { recursive: true, mode: DIR_MODE });
|
|
@@ -15634,6 +15636,79 @@ function mutate(code, language, seed = "pretense", opts = {}) {
|
|
|
15634
15636
|
}
|
|
15635
15637
|
};
|
|
15636
15638
|
}
|
|
15639
|
+
function mutateNamedIdentifiers(code, language, identifiers, seed = "pretense", opts = {}) {
|
|
15640
|
+
const t0 = performance.now();
|
|
15641
|
+
const lang = language ?? detectLanguage(code);
|
|
15642
|
+
const map = /* @__PURE__ */ new Map();
|
|
15643
|
+
if (!code?.trim() || identifiers.length === 0) {
|
|
15644
|
+
return { mutatedCode: code ?? "", map, stats: { tokensScanned: identifiers.length, tokensMutated: 0, durationMs: 0, language: lang } };
|
|
15645
|
+
}
|
|
15646
|
+
const signingKey = resolveSigningKey(opts);
|
|
15647
|
+
const usedSynthetics = /* @__PURE__ */ new Set();
|
|
15648
|
+
for (const { name, category } of identifiers) {
|
|
15649
|
+
if (!name || name.length <= 1 || SKIP_IDENTIFIERS2.has(name)) continue;
|
|
15650
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) continue;
|
|
15651
|
+
if (map.has(name)) continue;
|
|
15652
|
+
let synthetic = generateSyntheticName(name, seed, category, signingKey);
|
|
15653
|
+
let attempt = 0;
|
|
15654
|
+
while ((usedSynthetics.has(synthetic) || synthetic === name || code.includes(synthetic)) && attempt < 100) {
|
|
15655
|
+
attempt++;
|
|
15656
|
+
synthetic = generateSyntheticName(name, `${seed}:${attempt}`, category, signingKey);
|
|
15657
|
+
}
|
|
15658
|
+
if (synthetic !== name && !usedSynthetics.has(synthetic) && !code.includes(synthetic)) {
|
|
15659
|
+
map.set(name, synthetic);
|
|
15660
|
+
usedSynthetics.add(synthetic);
|
|
15661
|
+
}
|
|
15662
|
+
}
|
|
15663
|
+
const entries = [...map.entries()].sort((a, b) => b[0].length - a[0].length);
|
|
15664
|
+
let mutatedCode = code;
|
|
15665
|
+
const applied = /* @__PURE__ */ new Map();
|
|
15666
|
+
for (const [original, synthetic] of entries) {
|
|
15667
|
+
const next = replaceAll(mutatedCode, [[original, synthetic]], lang, true);
|
|
15668
|
+
if (next !== mutatedCode) {
|
|
15669
|
+
applied.set(original, synthetic);
|
|
15670
|
+
mutatedCode = next;
|
|
15671
|
+
}
|
|
15672
|
+
}
|
|
15673
|
+
return {
|
|
15674
|
+
mutatedCode,
|
|
15675
|
+
map: applied,
|
|
15676
|
+
stats: {
|
|
15677
|
+
tokensScanned: identifiers.length,
|
|
15678
|
+
tokensMutated: applied.size,
|
|
15679
|
+
durationMs: Math.round((performance.now() - t0) * 100) / 100,
|
|
15680
|
+
language: lang
|
|
15681
|
+
}
|
|
15682
|
+
};
|
|
15683
|
+
}
|
|
15684
|
+
var FINGERPRINT_CATEGORIES = [
|
|
15685
|
+
["functions", "function"],
|
|
15686
|
+
["classes", "class"],
|
|
15687
|
+
["constants", "constant"],
|
|
15688
|
+
["envVars", "variable"]
|
|
15689
|
+
];
|
|
15690
|
+
function loadFingerprintIdentifiers(projectRoot) {
|
|
15691
|
+
const path2 = join4(projectRoot, ".pretense", "fingerprint.json");
|
|
15692
|
+
if (!existsSync4(path2)) return [];
|
|
15693
|
+
let parsed;
|
|
15694
|
+
try {
|
|
15695
|
+
parsed = JSON.parse(readFileSync32(path2, "utf-8"));
|
|
15696
|
+
} catch {
|
|
15697
|
+
return [];
|
|
15698
|
+
}
|
|
15699
|
+
const out = [];
|
|
15700
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15701
|
+
for (const [key, category] of FINGERPRINT_CATEGORIES) {
|
|
15702
|
+
const arr = parsed[key];
|
|
15703
|
+
if (!Array.isArray(arr)) continue;
|
|
15704
|
+
for (const name of arr) {
|
|
15705
|
+
if (typeof name !== "string" || seen.has(name)) continue;
|
|
15706
|
+
seen.add(name);
|
|
15707
|
+
out.push({ name, category });
|
|
15708
|
+
}
|
|
15709
|
+
}
|
|
15710
|
+
return out;
|
|
15711
|
+
}
|
|
15637
15712
|
function reverse(mutatedCode, map) {
|
|
15638
15713
|
if (!mutatedCode || map.size === 0) return mutatedCode;
|
|
15639
15714
|
assertNoReplacementCollision(map.entries());
|
|
@@ -15940,7 +16015,7 @@ var RiskScorer = class {
|
|
|
15940
16015
|
// ../packages/store/dist/index.js
|
|
15941
16016
|
init_esm_shims();
|
|
15942
16017
|
import { createRequire as createRequire2 } from "module";
|
|
15943
|
-
import { mkdirSync as mkdirSync22, existsSync as
|
|
16018
|
+
import { mkdirSync as mkdirSync22, existsSync as existsSync6, chmodSync as chmodSync2, statSync as statSync2 } from "fs";
|
|
15944
16019
|
import { dirname as dirname2 } from "path";
|
|
15945
16020
|
import { homedir as homedir22 } from "os";
|
|
15946
16021
|
var nodeRequire = createRequire2(import.meta.url);
|
|
@@ -16001,7 +16076,7 @@ function openDatabase(path2) {
|
|
|
16001
16076
|
return sqliteBackend === "bun:sqlite" ? openBun(path2) : openBetterSqlite3(path2);
|
|
16002
16077
|
}
|
|
16003
16078
|
function ensureDbDirOwnerOnly(dir) {
|
|
16004
|
-
const preExisting =
|
|
16079
|
+
const preExisting = existsSync6(dir);
|
|
16005
16080
|
if (!preExisting) mkdirSync22(dir, { recursive: true, mode: 448 });
|
|
16006
16081
|
if (process.platform === "win32") return;
|
|
16007
16082
|
try {
|
|
@@ -16051,7 +16126,7 @@ var AuditStore = class {
|
|
|
16051
16126
|
if (!isMemory) {
|
|
16052
16127
|
for (const p of [resolvedPath, `${resolvedPath}-wal`, `${resolvedPath}-shm`]) {
|
|
16053
16128
|
try {
|
|
16054
|
-
if (
|
|
16129
|
+
if (existsSync6(p)) chmodSync2(p, 384);
|
|
16055
16130
|
} catch {
|
|
16056
16131
|
}
|
|
16057
16132
|
}
|
|
@@ -16432,8 +16507,8 @@ var PERIOD_DAYS = 7;
|
|
|
16432
16507
|
var PERIOD_MS = PERIOD_DAYS * 24 * 60 * 60 * 1e3;
|
|
16433
16508
|
|
|
16434
16509
|
// ../apps/proxy/dist/server.js
|
|
16435
|
-
import { readFileSync as readFileSync5, existsSync as
|
|
16436
|
-
import { join as
|
|
16510
|
+
import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
|
|
16511
|
+
import { join as join6 } from "path";
|
|
16437
16512
|
import { parse as parseYaml } from "yaml";
|
|
16438
16513
|
import { readFileSync as readFileSync23 } from "fs";
|
|
16439
16514
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -16531,12 +16606,12 @@ var DEFAULTS = {
|
|
|
16531
16606
|
};
|
|
16532
16607
|
function loadConfig(configPath) {
|
|
16533
16608
|
const paths = configPath ? [configPath] : [
|
|
16534
|
-
|
|
16535
|
-
|
|
16536
|
-
|
|
16609
|
+
join6(process.cwd(), "pretense.yaml"),
|
|
16610
|
+
join6(process.cwd(), "pretense.yml"),
|
|
16611
|
+
join6(process.env["HOME"] ?? "/tmp", ".pretense", "config.yaml")
|
|
16537
16612
|
];
|
|
16538
16613
|
for (const p of paths) {
|
|
16539
|
-
if (
|
|
16614
|
+
if (existsSync7(p)) {
|
|
16540
16615
|
try {
|
|
16541
16616
|
const raw2 = parseYaml(readFileSync5(p, "utf-8"));
|
|
16542
16617
|
return deepMerge(DEFAULTS, raw2);
|
|
@@ -16582,11 +16657,11 @@ function versionFromCliPackageJson() {
|
|
|
16582
16657
|
return void 0;
|
|
16583
16658
|
}
|
|
16584
16659
|
function productVersion() {
|
|
16585
|
-
return override ?? (true ? "0.6.
|
|
16660
|
+
return override ?? (true ? "0.6.22" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
|
|
16586
16661
|
}
|
|
16587
16662
|
var UNKNOWN_COMMIT = "unknown";
|
|
16588
16663
|
function bakedCommitSha() {
|
|
16589
|
-
return "
|
|
16664
|
+
return "6c0840ef4a9ecc06829215df3a27c5a1b1b603ab".length > 0 ? "6c0840ef4a9ecc06829215df3a27c5a1b1b603ab" : UNKNOWN_COMMIT;
|
|
16590
16665
|
}
|
|
16591
16666
|
var FEATURE_TIERS = {
|
|
16592
16667
|
compliance_export: "pro",
|
|
@@ -17591,6 +17666,7 @@ function createProxy(opts = {}) {
|
|
|
17591
17666
|
}
|
|
17592
17667
|
const app = new Hono2();
|
|
17593
17668
|
const derivation = { projectRoot: resolveProxyProjectRoot(opts.projectRoot) };
|
|
17669
|
+
const fingerprintIdentifiers = loadFingerprintIdentifiers(derivation.projectRoot);
|
|
17594
17670
|
let auditWriteFailures = 0;
|
|
17595
17671
|
let auditWriteFailureLogged = false;
|
|
17596
17672
|
const safeLog = (entry) => {
|
|
@@ -17958,9 +18034,16 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
|
|
|
17958
18034
|
continue;
|
|
17959
18035
|
}
|
|
17960
18036
|
const result = mutate(block.code, block.language, "pretense", derivation);
|
|
18037
|
+
let blockCode = result.mutatedCode;
|
|
17961
18038
|
for (const [k, v] of result.map.entries()) globalMap.set(k, v);
|
|
17962
18039
|
tokensMutated += result.stats.tokensMutated;
|
|
17963
|
-
|
|
18040
|
+
if (fingerprintIdentifiers.length > 0) {
|
|
18041
|
+
const fp = mutateNamedIdentifiers(blockCode, block.language, fingerprintIdentifiers, "pretense", derivation);
|
|
18042
|
+
for (const [k, v] of fp.map.entries()) globalMap.set(k, v);
|
|
18043
|
+
tokensMutated += fp.stats.tokensMutated;
|
|
18044
|
+
blockCode = fp.mutatedCode;
|
|
18045
|
+
}
|
|
18046
|
+
mutatedBlocks.push(blockCode);
|
|
17964
18047
|
}
|
|
17965
18048
|
return replaceCodeBlocks(text, blocks, mutatedBlocks);
|
|
17966
18049
|
});
|
|
@@ -18006,7 +18089,12 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
|
|
|
18006
18089
|
action: "blocked",
|
|
18007
18090
|
secretsBlocked: secretsTokenized,
|
|
18008
18091
|
piiRedacted: piiTokenized,
|
|
18009
|
-
|
|
18092
|
+
// TRUTHFUL COUNT: every span this proxy rewrote before egress, of any
|
|
18093
|
+
// kind (identifier tokens + tokenized secrets/PII) — the SAME number the
|
|
18094
|
+
// audit row logs as `mutationsApplied`. `tokensMutated` alone counted
|
|
18095
|
+
// only code identifiers, so a secret-only mutation pushed mutationCount:0
|
|
18096
|
+
// to the live dashboard even though a secret WAS mutated on the wire.
|
|
18097
|
+
mutationCount: mutationsApplied,
|
|
18010
18098
|
bytesScanned: fullText.length,
|
|
18011
18099
|
provider,
|
|
18012
18100
|
findings: riskResult.factors.map((f) => f.name),
|
|
@@ -18088,7 +18176,12 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
|
|
|
18088
18176
|
action: "error",
|
|
18089
18177
|
secretsBlocked: secretsTokenized,
|
|
18090
18178
|
piiRedacted: piiTokenized,
|
|
18091
|
-
|
|
18179
|
+
// TRUTHFUL COUNT: every span this proxy rewrote before egress, of any
|
|
18180
|
+
// kind (identifier tokens + tokenized secrets/PII) — the SAME number the
|
|
18181
|
+
// audit row logs as `mutationsApplied`. `tokensMutated` alone counted
|
|
18182
|
+
// only code identifiers, so a secret-only mutation pushed mutationCount:0
|
|
18183
|
+
// to the live dashboard even though a secret WAS mutated on the wire.
|
|
18184
|
+
mutationCount: mutationsApplied,
|
|
18092
18185
|
bytesScanned: originalSize,
|
|
18093
18186
|
provider,
|
|
18094
18187
|
findings: ["upstream_unreachable"],
|
|
@@ -18127,12 +18220,23 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
|
|
|
18127
18220
|
action: anyTokenized ? "mutated" : "allowed",
|
|
18128
18221
|
secretsBlocked: secretsTokenized,
|
|
18129
18222
|
piiRedacted: piiTokenized,
|
|
18130
|
-
|
|
18223
|
+
// TRUTHFUL COUNT: every span rewritten before egress (identifier tokens +
|
|
18224
|
+
// tokenized secrets/PII), matching the audit row's `mutationsApplied`. Using
|
|
18225
|
+
// `tokensMutated` here made the live dashboard show mutationCount:0 for a
|
|
18226
|
+
// request whose only mutation was a tokenized secret — the exact "meter logs
|
|
18227
|
+
// 0" defect. severity now reflects it too (mutationCount>0 -> 3, not 1).
|
|
18228
|
+
mutationCount: mutationsApplied,
|
|
18131
18229
|
bytesScanned: originalSize,
|
|
18132
18230
|
provider,
|
|
18133
|
-
//
|
|
18134
|
-
//
|
|
18135
|
-
|
|
18231
|
+
// Detector KINDS that fired (e.g. "aws_access_key") — metadata only, the same
|
|
18232
|
+
// kinds the audit row records as `detectorKinds`. NEVER secret VALUES or
|
|
18233
|
+
// identifier NAMES (the SSE bus carries the usage-upload privacy contract).
|
|
18234
|
+
// Plus the unscannable-skip marker so a dashboard/SIEM can alert on coverage
|
|
18235
|
+
// gaps rather than assume full coverage.
|
|
18236
|
+
findings: [
|
|
18237
|
+
...foundTypes,
|
|
18238
|
+
...skippedUnscannable > 0 ? ["skipped_unscannable_field"] : []
|
|
18239
|
+
],
|
|
18136
18240
|
sourceIp: c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip")
|
|
18137
18241
|
}));
|
|
18138
18242
|
if (anyTokenized) {
|
|
@@ -18263,11 +18367,11 @@ async function findFreePort(start, opts = {}) {
|
|
|
18263
18367
|
|
|
18264
18368
|
// src/proxy-pidfile.ts
|
|
18265
18369
|
init_esm_shims();
|
|
18266
|
-
import { existsSync as
|
|
18267
|
-
import { join as
|
|
18370
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, unlinkSync } from "fs";
|
|
18371
|
+
import { join as join7 } from "path";
|
|
18268
18372
|
import { homedir as homedir3 } from "os";
|
|
18269
18373
|
function pidFilePath(home = homedir3()) {
|
|
18270
|
-
return
|
|
18374
|
+
return join7(process.env.PRETENSE_PID_DIR ?? join7(home, ".pretense"), "proxy.pid");
|
|
18271
18375
|
}
|
|
18272
18376
|
function isProcessAlive(pid) {
|
|
18273
18377
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -18279,7 +18383,7 @@ function isProcessAlive(pid) {
|
|
|
18279
18383
|
}
|
|
18280
18384
|
}
|
|
18281
18385
|
function readProxyRecord(path2 = pidFilePath()) {
|
|
18282
|
-
if (!
|
|
18386
|
+
if (!existsSync8(path2)) return null;
|
|
18283
18387
|
try {
|
|
18284
18388
|
const parsed = JSON.parse(readFileSync6(path2, "utf-8"));
|
|
18285
18389
|
if (!Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
|
|
@@ -18306,7 +18410,7 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
18306
18410
|
async function stopProxy(path2 = pidFilePath(), graceMs = 2e3, pollMs = 50) {
|
|
18307
18411
|
const rec = readProxyRecord(path2);
|
|
18308
18412
|
if (!rec) {
|
|
18309
|
-
if (
|
|
18413
|
+
if (existsSync8(path2)) {
|
|
18310
18414
|
removeProxyRecord(path2);
|
|
18311
18415
|
return { kind: "stale", pid: 0, path: path2 };
|
|
18312
18416
|
}
|
|
@@ -18479,17 +18583,17 @@ import chalk3 from "chalk";
|
|
|
18479
18583
|
|
|
18480
18584
|
// src/plan-usage.ts
|
|
18481
18585
|
init_esm_shims();
|
|
18482
|
-
import { existsSync as
|
|
18483
|
-
import { join as
|
|
18586
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
18587
|
+
import { join as join8 } from "path";
|
|
18484
18588
|
import { homedir as homedir4 } from "os";
|
|
18485
18589
|
function credentialsPath() {
|
|
18486
|
-
return
|
|
18590
|
+
return join8(homedir4(), ".pretense", "credentials.json");
|
|
18487
18591
|
}
|
|
18488
18592
|
function readApiKey() {
|
|
18489
18593
|
const fromEnv = process.env["PRETENSE_API_KEY"]?.trim();
|
|
18490
18594
|
if (fromEnv) return fromEnv;
|
|
18491
18595
|
const path2 = credentialsPath();
|
|
18492
|
-
if (!
|
|
18596
|
+
if (!existsSync9(path2)) return null;
|
|
18493
18597
|
try {
|
|
18494
18598
|
const creds = JSON.parse(readFileSync7(path2, "utf-8"));
|
|
18495
18599
|
const key = creds["apiKey"] ?? creds["api_key"];
|
|
@@ -18911,7 +19015,7 @@ import { Command as Command5, Option, InvalidArgumentError as InvalidArgumentErr
|
|
|
18911
19015
|
import chalk5 from "chalk";
|
|
18912
19016
|
import { readFileSync as readFileSync8, statSync as statSync3, readdirSync as readdirSync2, openSync, readSync, closeSync } from "fs";
|
|
18913
19017
|
import { execSync as execSync3 } from "child_process";
|
|
18914
|
-
import { resolve as resolve3, join as
|
|
19018
|
+
import { resolve as resolve3, join as join9, extname as extname2, basename } from "path";
|
|
18915
19019
|
|
|
18916
19020
|
// src/safe-output.ts
|
|
18917
19021
|
init_esm_shims();
|
|
@@ -19101,9 +19205,9 @@ function walkDir(dir) {
|
|
|
19101
19205
|
for (const entry of entries) {
|
|
19102
19206
|
if (entry.isDirectory()) {
|
|
19103
19207
|
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
|
|
19104
|
-
files.push(...walkDir(
|
|
19208
|
+
files.push(...walkDir(join9(dir, entry.name)));
|
|
19105
19209
|
} else if (entry.isFile()) {
|
|
19106
|
-
files.push(
|
|
19210
|
+
files.push(join9(dir, entry.name));
|
|
19107
19211
|
}
|
|
19108
19212
|
}
|
|
19109
19213
|
return files;
|
|
@@ -19827,13 +19931,13 @@ function scanCommand() {
|
|
|
19827
19931
|
init_esm_shims();
|
|
19828
19932
|
import { Command as Command6 } from "commander";
|
|
19829
19933
|
import chalk6 from "chalk";
|
|
19830
|
-
import { existsSync as
|
|
19831
|
-
import { join as
|
|
19934
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
19935
|
+
import { join as join10 } from "path";
|
|
19832
19936
|
function statusCommand() {
|
|
19833
19937
|
return new Command6("status").description("Show Pretense protection status").action(async () => {
|
|
19834
19938
|
const dir = process.cwd();
|
|
19835
|
-
const fingerprintPath =
|
|
19836
|
-
const configPath =
|
|
19939
|
+
const fingerprintPath = join10(dir, ".pretense", "fingerprint.json");
|
|
19940
|
+
const configPath = join10(dir, "pretense.yaml");
|
|
19837
19941
|
console.log(chalk6.cyan("\n Pretense Status\n"));
|
|
19838
19942
|
const record = readProxyRecord(pidFilePath());
|
|
19839
19943
|
if (record && !isProcessAlive(record.pid)) {
|
|
@@ -19855,12 +19959,12 @@ function statusCommand() {
|
|
|
19855
19959
|
console.log(chalk6.yellow(` \u25CB Proxy not running ${chalk6.dim("(run: pretense start)")}`));
|
|
19856
19960
|
}
|
|
19857
19961
|
}
|
|
19858
|
-
if (
|
|
19962
|
+
if (existsSync10(configPath)) {
|
|
19859
19963
|
console.log(chalk6.green(` \u2713 Config: pretense.yaml`));
|
|
19860
19964
|
} else {
|
|
19861
19965
|
console.log(chalk6.yellow(` \u25CB No config ${chalk6.dim("(run: pretense config)")}`));
|
|
19862
19966
|
}
|
|
19863
|
-
if (
|
|
19967
|
+
if (existsSync10(fingerprintPath)) {
|
|
19864
19968
|
try {
|
|
19865
19969
|
const fp = JSON.parse(readFileSync9(fingerprintPath, "utf-8"));
|
|
19866
19970
|
const age = Math.round((Date.now() - fp.scannedAt) / (60 * 1e3));
|
|
@@ -19882,8 +19986,8 @@ function statusCommand() {
|
|
|
19882
19986
|
init_esm_shims();
|
|
19883
19987
|
import { Command as Command7 } from "commander";
|
|
19884
19988
|
import chalk7 from "chalk";
|
|
19885
|
-
import { writeFileSync as writeFileSync4, readFileSync as readFileSync10, existsSync as
|
|
19886
|
-
import { join as
|
|
19989
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
|
|
19990
|
+
import { join as join11 } from "path";
|
|
19887
19991
|
import YAML2 from "yaml";
|
|
19888
19992
|
var DEFAULT_CONFIG = {
|
|
19889
19993
|
version: 1,
|
|
@@ -19896,8 +20000,8 @@ function findConfigPath() {
|
|
|
19896
20000
|
const cwd = process.cwd();
|
|
19897
20001
|
const candidates = [".pretense.yaml", "pretense.yaml"];
|
|
19898
20002
|
for (const name of candidates) {
|
|
19899
|
-
const full =
|
|
19900
|
-
if (
|
|
20003
|
+
const full = join11(cwd, name);
|
|
20004
|
+
if (existsSync11(full)) return full;
|
|
19901
20005
|
}
|
|
19902
20006
|
return null;
|
|
19903
20007
|
}
|
|
@@ -19912,7 +20016,7 @@ function loadConfig2() {
|
|
|
19912
20016
|
}
|
|
19913
20017
|
}
|
|
19914
20018
|
function configFilePath() {
|
|
19915
|
-
return findConfigPath() ??
|
|
20019
|
+
return findConfigPath() ?? join11(process.cwd(), "pretense.yaml");
|
|
19916
20020
|
}
|
|
19917
20021
|
function getNestedValue(obj, key) {
|
|
19918
20022
|
const parts = key.split(".");
|
|
@@ -19983,7 +20087,7 @@ function configCommand() {
|
|
|
19983
20087
|
});
|
|
19984
20088
|
cmd.option("--force", "Overwrite existing config with defaults", false).action((opts) => {
|
|
19985
20089
|
if (opts.force) {
|
|
19986
|
-
const outPath =
|
|
20090
|
+
const outPath = join11(process.cwd(), "pretense.yaml");
|
|
19987
20091
|
writeFileSync4(outPath, YAML2.stringify(DEFAULT_CONFIG));
|
|
19988
20092
|
console.log(chalk7.green(`
|
|
19989
20093
|
\u2713 Created pretense.yaml
|
|
@@ -20000,14 +20104,14 @@ init_esm_shims();
|
|
|
20000
20104
|
import { Command as Command8 } from "commander";
|
|
20001
20105
|
import chalk8 from "chalk";
|
|
20002
20106
|
import { homedir as homedir5 } from "os";
|
|
20003
|
-
import { join as
|
|
20107
|
+
import { join as join12 } from "path";
|
|
20004
20108
|
function logsCommand() {
|
|
20005
20109
|
return new Command8("logs").description("Show recent audit log entries").option("-l, --limit <n>", "Number of entries to show", "50").option("--json", "Output as JSON", false).action((opts) => {
|
|
20006
20110
|
const limit = parseInt(opts.limit, 10);
|
|
20007
20111
|
let store;
|
|
20008
20112
|
let entries;
|
|
20009
20113
|
try {
|
|
20010
|
-
store = new AuditStore(
|
|
20114
|
+
store = new AuditStore(join12(homedir5(), ".pretense", "audit.db"));
|
|
20011
20115
|
entries = store.getRecentEntries(limit);
|
|
20012
20116
|
store.close();
|
|
20013
20117
|
} catch {
|
|
@@ -20047,7 +20151,7 @@ function logsCommand() {
|
|
|
20047
20151
|
init_esm_shims();
|
|
20048
20152
|
import { Command as Command9 } from "commander";
|
|
20049
20153
|
import chalk9 from "chalk";
|
|
20050
|
-
import { join as
|
|
20154
|
+
import { join as join13 } from "path";
|
|
20051
20155
|
import { homedir as homedir6 } from "os";
|
|
20052
20156
|
function renderBar(percent, width = 30) {
|
|
20053
20157
|
const filled = Math.min(Math.round(percent / 100 * width), width);
|
|
@@ -20087,7 +20191,7 @@ function emptyUsagePayload(resolution) {
|
|
|
20087
20191
|
};
|
|
20088
20192
|
}
|
|
20089
20193
|
function usageCommand() {
|
|
20090
|
-
return new Command9("usage").description("Show plan usage vs limits for the current billing period").option("--db <path>", "Path to audit database",
|
|
20194
|
+
return new Command9("usage").description("Show plan usage vs limits for the current billing period").option("--db <path>", "Path to audit database", join13(homedir6(), ".pretense", "audit.db")).option("--json", "Output as JSON", false).option("--offline", "Do not contact the plan service to resolve the tier", false).action(async (opts) => {
|
|
20091
20195
|
const resolution = await resolvePlanTier({ offline: opts.offline });
|
|
20092
20196
|
const tier = resolution.tier;
|
|
20093
20197
|
const collected = collectUsage(opts.db);
|
|
@@ -20238,12 +20342,12 @@ function usageCommand() {
|
|
|
20238
20342
|
init_esm_shims();
|
|
20239
20343
|
import { Command as Command10 } from "commander";
|
|
20240
20344
|
import chalk10 from "chalk";
|
|
20241
|
-
import { existsSync as
|
|
20242
|
-
import { join as
|
|
20345
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "fs";
|
|
20346
|
+
import { join as join14 } from "path";
|
|
20243
20347
|
import { homedir as homedir7 } from "os";
|
|
20244
20348
|
import { execSync as execSync4 } from "child_process";
|
|
20245
|
-
var CREDENTIALS_DIR =
|
|
20246
|
-
var CREDENTIALS_PATH =
|
|
20349
|
+
var CREDENTIALS_DIR = join14(homedir7(), ".pretense");
|
|
20350
|
+
var CREDENTIALS_PATH = join14(CREDENTIALS_DIR, "credentials.json");
|
|
20247
20351
|
async function verifyApiKey(apiKey) {
|
|
20248
20352
|
const url = `${getConfiguredApiRoot()}/api/cli/auth/validate`;
|
|
20249
20353
|
const timeoutMs = tierRequestTimeoutMs();
|
|
@@ -20290,7 +20394,7 @@ function storageNotice() {
|
|
|
20290
20394
|
`);
|
|
20291
20395
|
}
|
|
20292
20396
|
function readCredentials() {
|
|
20293
|
-
if (!
|
|
20397
|
+
if (!existsSync12(CREDENTIALS_PATH)) return null;
|
|
20294
20398
|
try {
|
|
20295
20399
|
return JSON.parse(readFileSync11(CREDENTIALS_PATH, "utf-8"));
|
|
20296
20400
|
} catch {
|
|
@@ -20424,7 +20528,7 @@ function authCommand() {
|
|
|
20424
20528
|
openUrl(authUrl);
|
|
20425
20529
|
});
|
|
20426
20530
|
auth.command("logout").description("Remove stored credentials").action(() => {
|
|
20427
|
-
if (!
|
|
20531
|
+
if (!existsSync12(CREDENTIALS_PATH)) {
|
|
20428
20532
|
console.log(chalk10.yellow("\n No credentials found. Already logged out.\n"));
|
|
20429
20533
|
return;
|
|
20430
20534
|
}
|
|
@@ -20479,7 +20583,7 @@ init_esm_shims();
|
|
|
20479
20583
|
import { Command as Command11 } from "commander";
|
|
20480
20584
|
import chalk11 from "chalk";
|
|
20481
20585
|
import { readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
|
|
20482
|
-
import { join as
|
|
20586
|
+
import { join as join15, extname as extname3, relative, isAbsolute } from "path";
|
|
20483
20587
|
var SUPPORTED_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
20484
20588
|
".ts",
|
|
20485
20589
|
".tsx",
|
|
@@ -20514,7 +20618,7 @@ function discoverFiles(dir) {
|
|
|
20514
20618
|
}
|
|
20515
20619
|
for (const entry of entries) {
|
|
20516
20620
|
if (entry.startsWith(".")) continue;
|
|
20517
|
-
const full =
|
|
20621
|
+
const full = join15(current, entry);
|
|
20518
20622
|
let stat;
|
|
20519
20623
|
try {
|
|
20520
20624
|
stat = statSync4(full);
|
|
@@ -20605,7 +20709,7 @@ function formatAgent(results, basePath) {
|
|
|
20605
20709
|
const agentResults = [];
|
|
20606
20710
|
for (const r of results) {
|
|
20607
20711
|
if (r.mutations.length === 0 && r.secrets.length === 0) continue;
|
|
20608
|
-
const filePath =
|
|
20712
|
+
const filePath = join15(basePath, r.file);
|
|
20609
20713
|
let lines;
|
|
20610
20714
|
try {
|
|
20611
20715
|
lines = readFileSync12(filePath, "utf-8").split("\n");
|
|
@@ -20676,7 +20780,7 @@ function formatInteractive(results) {
|
|
|
20676
20780
|
}
|
|
20677
20781
|
function reviewCommand() {
|
|
20678
20782
|
return new Command11("review").description("Preview mutations and secrets that would be applied").argument("[path]", "Directory to review (defaults to current directory)").option("--plain", "Plain text output", false).option("--json", "JSON array output with metadata", false).option("--agent", "Structured JSON optimized for AI agent consumption", false).option("--prompt-only", "Minimal output suitable for piping", false).option("--interactive", "Interactive formatted output (default)", false).action(async (path2, opts) => {
|
|
20679
|
-
const targetDir = path2 ? isAbsolute(path2) ? path2 :
|
|
20783
|
+
const targetDir = path2 ? isAbsolute(path2) ? path2 : join15(process.cwd(), path2) : process.cwd();
|
|
20680
20784
|
const files = discoverFiles(targetDir);
|
|
20681
20785
|
if (files.length === 0) {
|
|
20682
20786
|
if (opts?.json || opts?.agent) {
|
|
@@ -20712,13 +20816,13 @@ init_esm_shims();
|
|
|
20712
20816
|
import { Command as Command12 } from "commander";
|
|
20713
20817
|
import chalk12 from "chalk";
|
|
20714
20818
|
import {
|
|
20715
|
-
existsSync as
|
|
20819
|
+
existsSync as existsSync13,
|
|
20716
20820
|
writeFileSync as writeFileSync5,
|
|
20717
20821
|
chmodSync as chmodSync3,
|
|
20718
20822
|
readFileSync as readFileSync13,
|
|
20719
20823
|
mkdirSync as mkdirSync3
|
|
20720
20824
|
} from "fs";
|
|
20721
|
-
import { join as
|
|
20825
|
+
import { join as join16, resolve as resolve4 } from "path";
|
|
20722
20826
|
var HOOK_SCRIPTS = {
|
|
20723
20827
|
"pre-commit": `#!/bin/sh
|
|
20724
20828
|
# Pretense AI Firewall \u2014 pre-commit hook
|
|
@@ -20737,14 +20841,14 @@ exit $?
|
|
|
20737
20841
|
};
|
|
20738
20842
|
var VALID_MODES = ["pre-commit", "pre-push"];
|
|
20739
20843
|
function findGitDir(cwd) {
|
|
20740
|
-
const gitDir =
|
|
20741
|
-
if (
|
|
20844
|
+
const gitDir = join16(cwd, ".git");
|
|
20845
|
+
if (existsSync13(gitDir)) return gitDir;
|
|
20742
20846
|
return null;
|
|
20743
20847
|
}
|
|
20744
20848
|
function installHook(hooksDir, mode, force) {
|
|
20745
|
-
const hookPath =
|
|
20849
|
+
const hookPath = join16(hooksDir, mode);
|
|
20746
20850
|
const script = HOOK_SCRIPTS[mode];
|
|
20747
|
-
if (
|
|
20851
|
+
if (existsSync13(hookPath) && !force) {
|
|
20748
20852
|
const existing = readFileSync13(hookPath, "utf-8");
|
|
20749
20853
|
if (existing.includes("Pretense AI Firewall")) {
|
|
20750
20854
|
return { installed: false, path: hookPath, skipped: true };
|
|
@@ -20786,7 +20890,7 @@ function installCommand() {
|
|
|
20786
20890
|
);
|
|
20787
20891
|
process.exit(1);
|
|
20788
20892
|
}
|
|
20789
|
-
const hooksDir =
|
|
20893
|
+
const hooksDir = join16(gitDir, "hooks");
|
|
20790
20894
|
mkdirSync3(hooksDir, { recursive: true });
|
|
20791
20895
|
console.log(chalk12.cyan("\n Pretense hook installer\n"));
|
|
20792
20896
|
let hasError = false;
|
|
@@ -20823,22 +20927,22 @@ function installCommand() {
|
|
|
20823
20927
|
init_esm_shims();
|
|
20824
20928
|
import { Command as Command13 } from "commander";
|
|
20825
20929
|
import chalk13 from "chalk";
|
|
20826
|
-
import { appendFileSync as appendFileSync3, readFileSync as readFileSync14, existsSync as
|
|
20827
|
-
import { join as
|
|
20930
|
+
import { appendFileSync as appendFileSync3, readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
|
|
20931
|
+
import { join as join17 } from "path";
|
|
20828
20932
|
import { homedir as homedir8 } from "os";
|
|
20829
20933
|
var IGNORE_FILE = ".pretenseignore";
|
|
20830
|
-
var LAST_SCAN_PATH =
|
|
20934
|
+
var LAST_SCAN_PATH = join17(homedir8(), ".pretense", "last-scan.json");
|
|
20831
20935
|
function ignorePath() {
|
|
20832
|
-
return
|
|
20936
|
+
return join17(process.cwd(), IGNORE_FILE);
|
|
20833
20937
|
}
|
|
20834
20938
|
function readIgnoreFile() {
|
|
20835
20939
|
const p = ignorePath();
|
|
20836
|
-
if (!
|
|
20940
|
+
if (!existsSync14(p)) return [];
|
|
20837
20941
|
return readFileSync14(p, "utf-8").split("\n").filter((l) => l.length > 0);
|
|
20838
20942
|
}
|
|
20839
20943
|
function appendPattern(pattern) {
|
|
20840
20944
|
const p = ignorePath();
|
|
20841
|
-
const content =
|
|
20945
|
+
const content = existsSync14(p) ? readFileSync14(p, "utf-8") : "";
|
|
20842
20946
|
const lines = content.split("\n").filter((l) => l.length > 0);
|
|
20843
20947
|
if (lines.includes(pattern)) {
|
|
20844
20948
|
console.log(chalk13.yellow(`
|
|
@@ -20877,7 +20981,7 @@ function ignoreCommand() {
|
|
|
20877
20981
|
return;
|
|
20878
20982
|
}
|
|
20879
20983
|
if (opts.lastFound) {
|
|
20880
|
-
if (!
|
|
20984
|
+
if (!existsSync14(LAST_SCAN_PATH)) {
|
|
20881
20985
|
console.error(chalk13.red(`
|
|
20882
20986
|
\u2717 No last scan results found at ${LAST_SCAN_PATH}
|
|
20883
20987
|
`));
|
|
@@ -21135,8 +21239,8 @@ function completionCommand() {
|
|
|
21135
21239
|
init_esm_shims();
|
|
21136
21240
|
import { Command as Command15 } from "commander";
|
|
21137
21241
|
import chalk14 from "chalk";
|
|
21138
|
-
import { existsSync as
|
|
21139
|
-
import { join as
|
|
21242
|
+
import { existsSync as existsSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
21243
|
+
import { join as join18, resolve as resolve5 } from "path";
|
|
21140
21244
|
var DEFAULT_CONFIG_YAML = `scan:
|
|
21141
21245
|
secrets: block
|
|
21142
21246
|
pii: redact
|
|
@@ -21176,9 +21280,9 @@ function quickstartCommand() {
|
|
|
21176
21280
|
process.exit(1);
|
|
21177
21281
|
}
|
|
21178
21282
|
console.log(chalk14.dim(" [2/4] Writing config..."));
|
|
21179
|
-
const configPath =
|
|
21283
|
+
const configPath = join18(dir, "pretense.yaml");
|
|
21180
21284
|
if (!opts.dryRun) {
|
|
21181
|
-
if (!
|
|
21285
|
+
if (!existsSync15(configPath)) {
|
|
21182
21286
|
writeFileSync6(configPath, DEFAULT_CONFIG_YAML);
|
|
21183
21287
|
console.log(chalk14.green(" Created pretense.yaml"));
|
|
21184
21288
|
} else {
|
|
@@ -21187,8 +21291,8 @@ function quickstartCommand() {
|
|
|
21187
21291
|
}
|
|
21188
21292
|
console.log(chalk14.dim(" [3/4] Updating .gitignore..."));
|
|
21189
21293
|
if (!opts.dryRun) {
|
|
21190
|
-
const gitignorePath =
|
|
21191
|
-
let content =
|
|
21294
|
+
const gitignorePath = join18(dir, ".gitignore");
|
|
21295
|
+
let content = existsSync15(gitignorePath) ? (await import("fs")).default.readFileSync(gitignorePath, "utf-8") : "";
|
|
21192
21296
|
if (!content.includes(".pretense/")) {
|
|
21193
21297
|
content = content + (content.endsWith("\n") ? "" : "\n") + ".pretense/\n";
|
|
21194
21298
|
writeFileSync6(gitignorePath, content);
|
|
@@ -21218,7 +21322,7 @@ function quickstartCommand() {
|
|
|
21218
21322
|
init_esm_shims();
|
|
21219
21323
|
import { Command as Command16, Option as Option2 } from "commander";
|
|
21220
21324
|
import chalk16 from "chalk";
|
|
21221
|
-
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, chmodSync as chmodSync4, existsSync as
|
|
21325
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, chmodSync as chmodSync4, existsSync as existsSync16 } from "fs";
|
|
21222
21326
|
import { createHash as createHash3 } from "crypto";
|
|
21223
21327
|
import { resolve as resolve6, extname as extname4 } from "path";
|
|
21224
21328
|
|
|
@@ -21226,8 +21330,8 @@ import { resolve as resolve6, extname as extname4 } from "path";
|
|
|
21226
21330
|
init_esm_shims();
|
|
21227
21331
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, hkdfSync as hkdfSync2 } from "crypto";
|
|
21228
21332
|
import { homedir as homedir9 } from "os";
|
|
21229
|
-
import { join as
|
|
21230
|
-
var KEYS_DIR =
|
|
21333
|
+
import { join as join19 } from "path";
|
|
21334
|
+
var KEYS_DIR = join19(homedir9(), ".pretense", "keys");
|
|
21231
21335
|
var HKDF_SALT2 = Buffer.from("pretense-maps-v1", "utf-8");
|
|
21232
21336
|
var HKDF_INFO2 = Buffer.from("secret-mapping-aes-256-gcm", "utf-8");
|
|
21233
21337
|
var IV_LEN = 12;
|
|
@@ -21272,13 +21376,13 @@ import {
|
|
|
21272
21376
|
fchmodSync
|
|
21273
21377
|
} from "fs";
|
|
21274
21378
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
21275
|
-
import { dirname as dirname4, basename as basename2, join as
|
|
21379
|
+
import { dirname as dirname4, basename as basename2, join as join20 } from "path";
|
|
21276
21380
|
function writeFileAtomicSync(targetPath, data, opts = {}) {
|
|
21277
21381
|
let resolved;
|
|
21278
21382
|
try {
|
|
21279
21383
|
resolved = realpathSync2(targetPath);
|
|
21280
21384
|
} catch {
|
|
21281
|
-
resolved =
|
|
21385
|
+
resolved = join20(realpathSync2(dirname4(targetPath)), basename2(targetPath));
|
|
21282
21386
|
}
|
|
21283
21387
|
let mode = opts.mode;
|
|
21284
21388
|
if (mode === void 0) {
|
|
@@ -21289,7 +21393,7 @@ function writeFileAtomicSync(targetPath, data, opts = {}) {
|
|
|
21289
21393
|
}
|
|
21290
21394
|
}
|
|
21291
21395
|
const dir = dirname4(resolved);
|
|
21292
|
-
const tmp =
|
|
21396
|
+
const tmp = join20(dir, `.${basename2(resolved)}.pretense-${randomBytes4(6).toString("hex")}.tmp`);
|
|
21293
21397
|
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "utf-8");
|
|
21294
21398
|
let fd = openSync2(tmp, "wx", 384);
|
|
21295
21399
|
try {
|
|
@@ -21330,13 +21434,13 @@ function writeFileAtomicSync(targetPath, data, opts = {}) {
|
|
|
21330
21434
|
// src/default-map.ts
|
|
21331
21435
|
init_esm_shims();
|
|
21332
21436
|
import { createHash as createHash2 } from "crypto";
|
|
21333
|
-
import { join as
|
|
21437
|
+
import { join as join21 } from "path";
|
|
21334
21438
|
function mapsDir(override2) {
|
|
21335
|
-
return override2 ??
|
|
21439
|
+
return override2 ?? join21(pretenseHome(), "maps");
|
|
21336
21440
|
}
|
|
21337
21441
|
function defaultMapPath(absFilePath, override2) {
|
|
21338
21442
|
const digest = createHash2("sha256").update(absFilePath, "utf8").digest("hex");
|
|
21339
|
-
return
|
|
21443
|
+
return join21(mapsDir(override2), `${digest}.json`);
|
|
21340
21444
|
}
|
|
21341
21445
|
function ensureDefaultMapPath(absFilePath, override2) {
|
|
21342
21446
|
ensureDirOwnerOnly(mapsDir(override2));
|
|
@@ -21346,11 +21450,11 @@ function ensureDefaultMapPath(absFilePath, override2) {
|
|
|
21346
21450
|
// src/local-audit.ts
|
|
21347
21451
|
init_esm_shims();
|
|
21348
21452
|
import { homedir as homedir10 } from "os";
|
|
21349
|
-
import { join as
|
|
21453
|
+
import { join as join24 } from "path";
|
|
21350
21454
|
import { randomUUID } from "crypto";
|
|
21351
21455
|
import chalk15 from "chalk";
|
|
21352
21456
|
function localAuditDbPath() {
|
|
21353
|
-
return
|
|
21457
|
+
return join24(homedir10(), ".pretense", "audit.db");
|
|
21354
21458
|
}
|
|
21355
21459
|
function recordLocalAudit(rec, dbPath = localAuditDbPath()) {
|
|
21356
21460
|
let store = null;
|
|
@@ -21449,7 +21553,7 @@ async function uploadUsage(event) {
|
|
|
21449
21553
|
// src/commands/mutate.ts
|
|
21450
21554
|
var REVERSAL_MAP_VERSION = 3;
|
|
21451
21555
|
function preflightReversalMap(mapPath, absFile, content, contentSha, force) {
|
|
21452
|
-
if (!
|
|
21556
|
+
if (!existsSync16(mapPath)) return { action: "write", generation: 1 };
|
|
21453
21557
|
let existing;
|
|
21454
21558
|
try {
|
|
21455
21559
|
const parsed = JSON.parse(readFileSync15(mapPath, "utf-8"));
|
|
@@ -21626,7 +21730,25 @@ async function runMutate(file, opts = {}) {
|
|
|
21626
21730
|
opts.level,
|
|
21627
21731
|
opts.presetName
|
|
21628
21732
|
);
|
|
21629
|
-
|
|
21733
|
+
const absFile = resolve6(file);
|
|
21734
|
+
const projectRoot = resolveProjectRoot(absFile);
|
|
21735
|
+
const secretResult = mutateSecrets(content, findings, { projectRoot, keysDir: opts.keysDir });
|
|
21736
|
+
const fingerprintIds = loadFingerprintIdentifiers(projectRoot);
|
|
21737
|
+
const idMut = fingerprintIds.length > 0 ? mutateNamedIdentifiers(
|
|
21738
|
+
secretResult.content,
|
|
21739
|
+
L2_LANGUAGE_BY_EXT2[extname4(file).toLowerCase()],
|
|
21740
|
+
fingerprintIds,
|
|
21741
|
+
"pretense",
|
|
21742
|
+
{ projectRoot, keysDir: opts.keysDir }
|
|
21743
|
+
) : { mutatedCode: secretResult.content, map: /* @__PURE__ */ new Map() };
|
|
21744
|
+
const finalContent = idMut.mutatedCode;
|
|
21745
|
+
const identifierMutations = [...idMut.map.entries()].map(([original, replacement]) => ({
|
|
21746
|
+
original,
|
|
21747
|
+
replacement,
|
|
21748
|
+
type: "proprietary"
|
|
21749
|
+
}));
|
|
21750
|
+
const totalMutations = secretResult.mutations.length + identifierMutations.length;
|
|
21751
|
+
if (totalMutations === 0) {
|
|
21630
21752
|
if (unmutatable.size === 0) {
|
|
21631
21753
|
console.error(chalk16.dim(` No secrets found in ${file} \u2014 nothing to mutate.`));
|
|
21632
21754
|
} else {
|
|
@@ -21636,9 +21758,10 @@ async function runMutate(file, opts = {}) {
|
|
|
21636
21758
|
return 0;
|
|
21637
21759
|
}
|
|
21638
21760
|
reportUnmutatable(unmutatable);
|
|
21639
|
-
const
|
|
21640
|
-
|
|
21641
|
-
|
|
21761
|
+
const result = {
|
|
21762
|
+
content: finalContent,
|
|
21763
|
+
mutations: [...secretResult.mutations, ...identifierMutations]
|
|
21764
|
+
};
|
|
21642
21765
|
let effectiveMap;
|
|
21643
21766
|
if (preview) {
|
|
21644
21767
|
effectiveMap = void 0;
|
|
@@ -21710,13 +21833,21 @@ async function runMutate(file, opts = {}) {
|
|
|
21710
21833
|
);
|
|
21711
21834
|
return 1;
|
|
21712
21835
|
}
|
|
21836
|
+
const secretCount = secretResult.mutations.length;
|
|
21837
|
+
const idCount = identifierMutations.length;
|
|
21838
|
+
const summary = () => {
|
|
21839
|
+
const parts = [];
|
|
21840
|
+
if (secretCount > 0) parts.push(`${secretCount} secret${secretCount === 1 ? "" : "s"}`);
|
|
21841
|
+
if (idCount > 0) parts.push(`${idCount} registered identifier${idCount === 1 ? "" : "s"}`);
|
|
21842
|
+
return parts.join(" + ");
|
|
21843
|
+
};
|
|
21713
21844
|
try {
|
|
21714
21845
|
if (inPlace) {
|
|
21715
21846
|
writeFileAtomicSync(file, result.content);
|
|
21716
|
-
console.error(chalk16.green(` \u2713 Mutated ${
|
|
21847
|
+
console.error(chalk16.green(` \u2713 Mutated ${summary()} in ${file} (in-place)`));
|
|
21717
21848
|
} else if (opts.output) {
|
|
21718
21849
|
writeFileAtomicSync(opts.output, result.content);
|
|
21719
|
-
console.error(chalk16.green(` \u2713 Mutated ${
|
|
21850
|
+
console.error(chalk16.green(` \u2713 Mutated ${summary()} \u2192 ${opts.output}`));
|
|
21720
21851
|
} else {
|
|
21721
21852
|
process.stdout.write(result.content);
|
|
21722
21853
|
}
|
|
@@ -21733,7 +21864,9 @@ async function runMutate(file, opts = {}) {
|
|
|
21733
21864
|
let secretsBlocked = 0;
|
|
21734
21865
|
let piiRedacted = 0;
|
|
21735
21866
|
for (const m of result.mutations) {
|
|
21736
|
-
|
|
21867
|
+
const cat = categoryByType.get(m.type);
|
|
21868
|
+
if (cat === "pii") piiRedacted += 1;
|
|
21869
|
+
else if (m.type === "proprietary") continue;
|
|
21737
21870
|
else secretsBlocked += 1;
|
|
21738
21871
|
}
|
|
21739
21872
|
recordLocalAudit({
|
|
@@ -21795,7 +21928,7 @@ function mutateCommand() {
|
|
|
21795
21928
|
init_esm_shims();
|
|
21796
21929
|
import { Command as Command17 } from "commander";
|
|
21797
21930
|
import chalk17 from "chalk";
|
|
21798
|
-
import { readFileSync as readFileSync16, existsSync as
|
|
21931
|
+
import { readFileSync as readFileSync16, existsSync as existsSync17, statSync as statSync6 } from "fs";
|
|
21799
21932
|
import { resolve as resolve7 } from "path";
|
|
21800
21933
|
import { createHash as createHash4 } from "crypto";
|
|
21801
21934
|
function sha256Hex3(s) {
|
|
@@ -21856,14 +21989,14 @@ function loadReversalMap(mapPath) {
|
|
|
21856
21989
|
function runReverse(file, opts = {}) {
|
|
21857
21990
|
const reverseStartMs = Date.now();
|
|
21858
21991
|
const absPath = resolve7(file);
|
|
21859
|
-
if (!
|
|
21992
|
+
if (!existsSync17(absPath) || !statSync6(absPath).isFile()) {
|
|
21860
21993
|
console.error(chalk17.red(`
|
|
21861
21994
|
x File not found: ${file}
|
|
21862
21995
|
`));
|
|
21863
21996
|
return 1;
|
|
21864
21997
|
}
|
|
21865
21998
|
const mapPath = opts.map ?? defaultMapPath(absPath, opts.mapsDir);
|
|
21866
|
-
if (!opts.map && !
|
|
21999
|
+
if (!opts.map && !existsSync17(mapPath)) {
|
|
21867
22000
|
console.error(
|
|
21868
22001
|
chalk17.red(`
|
|
21869
22002
|
x No reversal map found for ${absPath}`) + chalk17.dim(`
|
|
@@ -22320,7 +22453,7 @@ init_esm_shims();
|
|
|
22320
22453
|
import { Command as Command20 } from "commander";
|
|
22321
22454
|
import chalk20 from "chalk";
|
|
22322
22455
|
import { homedir as homedir11 } from "os";
|
|
22323
|
-
import { join as
|
|
22456
|
+
import { join as join25 } from "path";
|
|
22324
22457
|
var VALID_FORMATS2 = ["table", "csv", "json", "jsonl"];
|
|
22325
22458
|
function toSafeAuditRow(e) {
|
|
22326
22459
|
return {
|
|
@@ -22444,7 +22577,7 @@ function auditCommand() {
|
|
|
22444
22577
|
`));
|
|
22445
22578
|
process.exit(1);
|
|
22446
22579
|
}
|
|
22447
|
-
const dbPath = opts.db ??
|
|
22580
|
+
const dbPath = opts.db ?? join25(homedir11(), ".pretense", "audit.db");
|
|
22448
22581
|
let rows;
|
|
22449
22582
|
try {
|
|
22450
22583
|
const store = new AuditStore(dbPath);
|
|
@@ -22504,7 +22637,7 @@ function auditCommand() {
|
|
|
22504
22637
|
init_esm_shims();
|
|
22505
22638
|
import { Command as Command21 } from "commander";
|
|
22506
22639
|
import chalk21 from "chalk";
|
|
22507
|
-
import { join as
|
|
22640
|
+
import { join as join26 } from "path";
|
|
22508
22641
|
import { homedir as homedir12 } from "os";
|
|
22509
22642
|
function remaining(used, limit) {
|
|
22510
22643
|
if (limit === null) return null;
|
|
@@ -22523,7 +22656,7 @@ function bar(used, limit, width = 24) {
|
|
|
22523
22656
|
return chalk21.green(s);
|
|
22524
22657
|
}
|
|
22525
22658
|
function creditsCommand() {
|
|
22526
|
-
return new Command21("credits").alias("tokens").description("Show remaining mutation budget for the current period").option("--db <path>", "Path to audit database",
|
|
22659
|
+
return new Command21("credits").alias("tokens").description("Show remaining mutation budget for the current period").option("--db <path>", "Path to audit database", join26(homedir12(), ".pretense", "audit.db")).option("--json", "Output as JSON", false).option("--offline", "Do not contact the plan service to resolve the tier", false).action(async (opts) => {
|
|
22527
22660
|
const resolution = await resolvePlanTier({ offline: opts.offline });
|
|
22528
22661
|
const tier = resolution.tier;
|
|
22529
22662
|
const collected = collectUsage(opts.db);
|
package/dist/postinstall.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pretense/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.22",
|
|
4
4
|
"description": "Local-first AI firewall that mutates proprietary code before sending to LLM APIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
"typescript": "^5.4.0",
|
|
25
25
|
"vitest": "^1.0.0",
|
|
26
26
|
"@pretense/billing": "0.1.0",
|
|
27
|
-
"@pretense/
|
|
27
|
+
"@pretense/compliance-engine": "0.1.0",
|
|
28
28
|
"@pretense/learner": "0.2.0",
|
|
29
29
|
"@pretense/mutator": "0.2.0",
|
|
30
|
-
"@pretense/
|
|
30
|
+
"@pretense/proxy": "0.1.0",
|
|
31
31
|
"@pretense/scanner": "0.2.0",
|
|
32
32
|
"@pretense/scanner-rs": "0.2.0",
|
|
33
33
|
"@pretense/store": "0.2.0",
|
|
34
|
-
"@pretense/
|
|
34
|
+
"@pretense/protocol": "0.1.0"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public",
|