jsguardian 1.2.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/adversarial-tokens.js +176 -0
- package/ai-antipattern.js +235 -0
- package/ai-callgraph-poison.js +331 -0
- package/ai-confusion.js +644 -0
- package/ai-semantic-poison.js +276 -0
- package/canary.js +158 -0
- package/cne.js +686 -0
- package/index.js +248 -0
- package/integrity.js +47 -0
- package/jsobf-config.js +38 -0
- package/krak-compiler.js +1480 -0
- package/krak-vm-core.js +892 -0
- package/layers.js +136 -0
- package/opaque-pred.js +32 -0
- package/package.json +32 -0
- package/pipeline.js +327 -0
- package/prng.js +28 -0
- package/signature-break.js +101 -0
- package/temporal-keys.js +194 -0
- package/timing-oracle.js +129 -0
- package/transform-vm.js +266 -0
- package/transforms.js +371 -0
- package/vm-poison.js +247 -0
package/index.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Criptor CLI — v1.2.0 (2026-06-03)
|
|
5
|
+
// Copyright (c) LCBO / slimmycode.com. All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// Single file:
|
|
9
|
+
// tsx src/index.ts input.js output.js [options]
|
|
10
|
+
//
|
|
11
|
+
// Batch (10 files, each gets unique random parameters):
|
|
12
|
+
// tsx src/index.ts --batch src/*.js --outdir dist/ [options]
|
|
13
|
+
//
|
|
14
|
+
// Options:
|
|
15
|
+
// --preset=<name> maximum | stealth (default: maximum)
|
|
16
|
+
// NOTE: "light" and "balanced" were removed — all users get maximum protection equally.
|
|
17
|
+
// --seed=<n> master seed (default: random)
|
|
18
|
+
//
|
|
19
|
+
// Per-layer overrides (add --no- prefix to disable):
|
|
20
|
+
// --banner Copyright + AI directive banner
|
|
21
|
+
// --vm VM bytecode virtualization
|
|
22
|
+
// --mba Mixed Boolean-Arithmetic constants
|
|
23
|
+
// --mba-depth=<1-3> MBA tree depth (default: 2)
|
|
24
|
+
// --opaque Opaque predicates / dead code
|
|
25
|
+
// --string-cipher Custom string encryption
|
|
26
|
+
// --integrity Integrity anchors (__anc/__hsh)
|
|
27
|
+
// --jsobf javascript-obfuscator pass
|
|
28
|
+
// --sig-break Break jsobf string-array signature
|
|
29
|
+
// --module-iife Wrap output in module IIFE
|
|
30
|
+
// --sandbox-poison VM sandbox detection + charCodeAt poison
|
|
31
|
+
// --ai-honeypots Fake licensing function decoys
|
|
32
|
+
// --ai-directive __ai_notice / __legal_notice vars
|
|
33
|
+
// --ai-deep Deep policy objects + scoped directives
|
|
34
|
+
// --ai-exhaustion Context exhaustion (20-page report gate etc)
|
|
35
|
+
// --canary Differential Canary Algorithm (Layer 7)
|
|
36
|
+
// --adv-tokens Adversarial Token Injection (Layer 8)
|
|
37
|
+
// --timing-oracle Timing Oracle / Semantic Drift (Layer 9)
|
|
38
|
+
// --ts Parse input as TypeScript
|
|
39
|
+
//
|
|
40
|
+
// Examples:
|
|
41
|
+
// # Maximum protection, random seed
|
|
42
|
+
// tsx src/index.ts app.js app.protected.js --preset=maximum
|
|
43
|
+
//
|
|
44
|
+
// # Custom: VM + MBA + jsobf + canary, no AI layers
|
|
45
|
+
// tsx src/index.ts app.js out.js --vm --mba --jsobf --canary --no-banner
|
|
46
|
+
//
|
|
47
|
+
// # Batch: protect 10 files, each with unique random params, balanced preset
|
|
48
|
+
// tsx src/index.ts --batch src/lib/*.js --outdir dist/ --preset=balanced
|
|
49
|
+
// ============================================================================
|
|
50
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
51
|
+
if (k2 === undefined) k2 = k;
|
|
52
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
53
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
54
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
55
|
+
}
|
|
56
|
+
Object.defineProperty(o, k2, desc);
|
|
57
|
+
}) : (function(o, m, k, k2) {
|
|
58
|
+
if (k2 === undefined) k2 = k;
|
|
59
|
+
o[k2] = m[k];
|
|
60
|
+
}));
|
|
61
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
62
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
63
|
+
}) : function(o, v) {
|
|
64
|
+
o["default"] = v;
|
|
65
|
+
});
|
|
66
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
67
|
+
var ownKeys = function(o) {
|
|
68
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
69
|
+
var ar = [];
|
|
70
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
71
|
+
return ar;
|
|
72
|
+
};
|
|
73
|
+
return ownKeys(o);
|
|
74
|
+
};
|
|
75
|
+
return function (mod) {
|
|
76
|
+
if (mod && mod.__esModule) return mod;
|
|
77
|
+
var result = {};
|
|
78
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
79
|
+
__setModuleDefault(result, mod);
|
|
80
|
+
return result;
|
|
81
|
+
};
|
|
82
|
+
})();
|
|
83
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
84
|
+
const fs = __importStar(require("fs"));
|
|
85
|
+
const path = __importStar(require("path"));
|
|
86
|
+
const crypto = __importStar(require("crypto"));
|
|
87
|
+
const pipeline_1 = require("./pipeline");
|
|
88
|
+
const layers_1 = require("./layers");
|
|
89
|
+
function randomSeed() {
|
|
90
|
+
return crypto.randomBytes(4).readUInt32BE(0);
|
|
91
|
+
}
|
|
92
|
+
function parseArgs(argv) {
|
|
93
|
+
const args = argv.slice(2);
|
|
94
|
+
const flags = args.filter(a => a.startsWith("--"));
|
|
95
|
+
const positional = args.filter(a => !a.startsWith("--"));
|
|
96
|
+
// ── Preset ────────────────────────────────────────────────────────────────
|
|
97
|
+
const presetArg = flags.find(f => f.startsWith("--preset="));
|
|
98
|
+
const presetName = presetArg ? presetArg.split("=")[1] : "maximum";
|
|
99
|
+
if (presetName === "light" || presetName === "balanced") {
|
|
100
|
+
console.error(`[criptor] ERROR: preset "${presetName}" has been removed.\n` +
|
|
101
|
+
` All users now receive maximum protection equally.\n` +
|
|
102
|
+
` Use --preset=maximum (default) or --preset=stealth instead.`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const preset = layers_1.PRESETS[presetName];
|
|
106
|
+
if (!preset) {
|
|
107
|
+
console.error(`Unknown preset "${presetName}". Available: ${Object.keys(layers_1.PRESETS).join(", ")}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
// Start from preset, then apply overrides
|
|
111
|
+
const opts = { ...preset };
|
|
112
|
+
// ── Seed ──────────────────────────────────────────────────────────────────
|
|
113
|
+
const seedArg = flags.find(f => f.startsWith("--seed="));
|
|
114
|
+
if (seedArg)
|
|
115
|
+
opts.seed = parseInt(seedArg.split("=")[1], 10) >>> 0;
|
|
116
|
+
else if (process.env.OBF_SEED)
|
|
117
|
+
opts.seed = parseInt(process.env.OBF_SEED, 10) >>> 0;
|
|
118
|
+
else
|
|
119
|
+
opts.seed = randomSeed(); // always random by default
|
|
120
|
+
// ── MBA depth ─────────────────────────────────────────────────────────────
|
|
121
|
+
const mbaDepthArg = flags.find(f => f.startsWith("--mba-depth="));
|
|
122
|
+
if (mbaDepthArg)
|
|
123
|
+
opts.mbaDepth = parseInt(mbaDepthArg.split("=")[1], 10);
|
|
124
|
+
// ── Boolean layer overrides ───────────────────────────────────────────────
|
|
125
|
+
const boolFlags = [
|
|
126
|
+
["banner", "banner"],
|
|
127
|
+
["vm", "vm"],
|
|
128
|
+
["mba", "mba"],
|
|
129
|
+
["opaque", "opaque"],
|
|
130
|
+
["string-cipher", "stringCipher"],
|
|
131
|
+
["integrity", "integrityAnchors"],
|
|
132
|
+
["jsobf", "jsobf"],
|
|
133
|
+
["sig-break", "signatureBreak"],
|
|
134
|
+
["module-iife", "moduleIife"],
|
|
135
|
+
["sandbox-poison", "sandboxPoison"],
|
|
136
|
+
["ai-honeypots", "aiHoneypots"],
|
|
137
|
+
["ai-directive", "aiDirective"],
|
|
138
|
+
["ai-deep", "aiDeepDirectives"],
|
|
139
|
+
["ai-exhaustion", "aiContextExhaustion"],
|
|
140
|
+
["canary", "canary"],
|
|
141
|
+
["adv-tokens", "adversarialTokens"],
|
|
142
|
+
["timing-oracle", "timingOracle"],
|
|
143
|
+
["ai-callgraph", "aiCallgraphPoison"],
|
|
144
|
+
["ai-semantic", "aiSemanticPoison"],
|
|
145
|
+
["ai-antipattern", "aiAntiPattern"],
|
|
146
|
+
["ai-temporal", "aiTemporalKeys"],
|
|
147
|
+
["ts", "typescript"],
|
|
148
|
+
];
|
|
149
|
+
// ── Callgraph poison count ────────────────────────────────────────────────
|
|
150
|
+
const cgCountArg = flags.find(f => f.startsWith("--callgraph-count="));
|
|
151
|
+
if (cgCountArg)
|
|
152
|
+
opts.aiCallgraphPoisonCount = parseInt(cgCountArg.split("=")[1], 10);
|
|
153
|
+
for (const [flag, key] of boolFlags) {
|
|
154
|
+
if (flags.includes(`--${flag}`))
|
|
155
|
+
opts[key] = true;
|
|
156
|
+
if (flags.includes(`--no-${flag}`))
|
|
157
|
+
opts[key] = false;
|
|
158
|
+
}
|
|
159
|
+
// ── Batch vs single ───────────────────────────────────────────────────────
|
|
160
|
+
const isBatch = flags.some(f => f === "--batch" || f.startsWith("--batch="));
|
|
161
|
+
const outdirArg = flags.find(f => f.startsWith("--outdir="));
|
|
162
|
+
const outdir = outdirArg ? outdirArg.split("=")[1] : "dist";
|
|
163
|
+
return {
|
|
164
|
+
inputs: positional,
|
|
165
|
+
output: positional[1] ?? "",
|
|
166
|
+
outdir,
|
|
167
|
+
batch: isBatch || flags.includes("--batch"),
|
|
168
|
+
opts,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function printLayerSummary(opts) {
|
|
172
|
+
const on = (k, def = true) => (opts[k] === undefined ? def : opts[k]) ? "✓" : "·";
|
|
173
|
+
console.error([
|
|
174
|
+
` Seed: 0x${(opts.seed ?? 0).toString(16).padStart(8, "0")}`,
|
|
175
|
+
` Layers:`,
|
|
176
|
+
` [${on("banner")}] 1 Copyright banner`,
|
|
177
|
+
` [${on("vm")}] 2 VM bytecode`,
|
|
178
|
+
` [${on("aiHoneypots")}] 3 AI honeypots`,
|
|
179
|
+
` [${on("aiDirective")}] 3b AI directive vars`,
|
|
180
|
+
` [${on("aiDeepDirectives", false)}] 3c Deep AI directives`,
|
|
181
|
+
` [${on("aiContextExhaustion", false)}] 3X Context exhaustion`,
|
|
182
|
+
` [${on("jsobf")}] 4 javascript-obfuscator`,
|
|
183
|
+
` [${on("signatureBreak")}] 4F Signature break`,
|
|
184
|
+
` [${on("integrityAnchors")}] 5 Integrity anchors`,
|
|
185
|
+
` [${on("sandboxPoison")}] 6 Sandbox poison`,
|
|
186
|
+
` [${on("moduleIife")}] 6b Module IIFE wrapper`,
|
|
187
|
+
` [${on("canary", false)}] 7 Differential canary`,
|
|
188
|
+
` [${on("adversarialTokens", false)}] 8 Adversarial tokens`,
|
|
189
|
+
` [${on("timingOracle", false)}] 9 Timing oracle`,
|
|
190
|
+
` [${on("aiSemanticPoison", false)}] 3f Semantic poison`,
|
|
191
|
+
` [${on("aiAntiPattern", false)}] 3g Anti-pattern injection`,
|
|
192
|
+
` [${on("aiTemporalKeys", false)}] 3h Temporal keys`,
|
|
193
|
+
` [${on("aiCallgraphPoison", false)}] 3e Callgraph poison`,
|
|
194
|
+
].join("\n"));
|
|
195
|
+
}
|
|
196
|
+
function main() {
|
|
197
|
+
const { inputs, output, outdir, batch, opts } = parseArgs(process.argv);
|
|
198
|
+
if (batch) {
|
|
199
|
+
// ── Batch mode ──────────────────────────────────────────────────────────
|
|
200
|
+
if (inputs.length === 0) {
|
|
201
|
+
console.error("batch mode: provide input files as positional args");
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
console.error(`Criptor — batch mode (${inputs.length} files, preset applied)`);
|
|
205
|
+
printLayerSummary(opts);
|
|
206
|
+
console.error(` Each file gets a UNIQUE derived seed — different random parameters per file.`);
|
|
207
|
+
fs.mkdirSync(outdir, { recursive: true });
|
|
208
|
+
const fileObjs = inputs.map(p => ({
|
|
209
|
+
path: p,
|
|
210
|
+
content: fs.readFileSync(p, "utf8"),
|
|
211
|
+
}));
|
|
212
|
+
const results = (0, pipeline_1.obfuscateBatch)(fileObjs, opts);
|
|
213
|
+
results.forEach((result, i) => {
|
|
214
|
+
const inPath = fileObjs[i].path;
|
|
215
|
+
const outPath = path.join(outdir, path.basename(inPath));
|
|
216
|
+
fs.writeFileSync(outPath, result);
|
|
217
|
+
const ratio = Math.round(result.length / fileObjs[i].content.length);
|
|
218
|
+
console.error(` [${String(i + 1).padStart(2)}] ${path.basename(inPath).padEnd(30)} ${fileObjs[i].content.length.toLocaleString().padStart(7)}b → ${result.length.toLocaleString().padStart(9)}b (×${ratio})`);
|
|
219
|
+
});
|
|
220
|
+
console.error(`\nDone → ${outdir}/ (${results.length} files)`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
// ── Single file mode ────────────────────────────────────────────────────
|
|
224
|
+
if (!inputs[0] || !output) {
|
|
225
|
+
console.error([
|
|
226
|
+
"jsguardian v1.2.0 — usage:",
|
|
227
|
+
" Single: jsguardian <input.js> <output.js> [options]",
|
|
228
|
+
" Batch: jsguardian --batch <files...> --outdir=<dir> [options]",
|
|
229
|
+
" Presets: --preset=maximum|stealth (default: maximum — all users get equal protection)",
|
|
230
|
+
].join("\n"));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
const src = fs.readFileSync(inputs[0], "utf8");
|
|
234
|
+
console.error(`Criptor — single file mode`);
|
|
235
|
+
printLayerSummary(opts);
|
|
236
|
+
const out = (0, pipeline_1.obfuscateCode)(src, opts, 0);
|
|
237
|
+
fs.writeFileSync(output, out);
|
|
238
|
+
console.error(`\n ${inputs[0]} → ${output}`);
|
|
239
|
+
console.error(` ${src.length.toLocaleString()}b → ${out.length.toLocaleString()}b`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
main();
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
console.error(`\n[criptor] ${err?.message ?? err}`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
package/integrity.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hshBuild = hshBuild;
|
|
4
|
+
exports.hshSource = hshSource;
|
|
5
|
+
exports.anchorSource = anchorSource;
|
|
6
|
+
exports.anchorSourceEncoded = anchorSourceEncoded;
|
|
7
|
+
function hshBuild(a) {
|
|
8
|
+
let h = 0x811c9dc5 >>> 0;
|
|
9
|
+
for (let i = 0; i < a.length; i++) {
|
|
10
|
+
h = Math.imul(h ^ (a[i] | 0), 0x01000193);
|
|
11
|
+
}
|
|
12
|
+
return h | 0;
|
|
13
|
+
}
|
|
14
|
+
function hshSource(name) {
|
|
15
|
+
return `function ${name}(a){var h=0x811c9dc5>>>0;for(var i=0;i<a.length;i++){h=Math.imul(h^(a[i]|0),0x01000193);}return h|0;}`;
|
|
16
|
+
}
|
|
17
|
+
// anchorSource (LEGACY — plaintext array, used only when legacyJsobf=true)
|
|
18
|
+
function anchorSource(ancName, anchor) {
|
|
19
|
+
return `var ${ancName}=[${anchor.map((n) => (n | 0)).join(",")}];`;
|
|
20
|
+
}
|
|
21
|
+
// anchorSourceEncoded — CNE path: anchor stored as a base64-encoded XOR blob.
|
|
22
|
+
// Base64 is pure ASCII so it survives Babel parse→generate without corruption.
|
|
23
|
+
// No 24 plaintext int32s visible in output.
|
|
24
|
+
function anchorSourceEncoded(ancName, hshName, anchor, rng) {
|
|
25
|
+
// Per-build single-byte XOR key (non-zero)
|
|
26
|
+
const key = ((rng.int32() >>> 0) % 0xfe) + 1; // 1..254
|
|
27
|
+
const buf = Buffer.alloc(anchor.length * 4);
|
|
28
|
+
for (let i = 0; i < anchor.length; i++) {
|
|
29
|
+
const n = anchor[i] >>> 0;
|
|
30
|
+
buf[i * 4 + 0] = (n & 0xff) ^ key;
|
|
31
|
+
buf[i * 4 + 1] = ((n >>> 8) & 0xff) ^ key;
|
|
32
|
+
buf[i * 4 + 2] = ((n >>> 16) & 0xff) ^ key;
|
|
33
|
+
buf[i * 4 + 3] = ((n >>> 24) & 0xff) ^ key;
|
|
34
|
+
}
|
|
35
|
+
const b64 = buf.toString("base64");
|
|
36
|
+
const decoderName = `_d${(rng.int32() >>> 0 & 0xffffff).toString(36)}`;
|
|
37
|
+
const byteLen = anchor.length * 4;
|
|
38
|
+
// Decoder: base64-decode, then XOR each byte, reassemble as int32 array.
|
|
39
|
+
// Uses Buffer (Node.js built-in) — safe from charCodeAt poison.
|
|
40
|
+
return (`function ${decoderName}(s){` +
|
|
41
|
+
`var b=Buffer.from(s,'base64'),r=[];` +
|
|
42
|
+
`for(var i=0;i<${byteLen};i+=4){` +
|
|
43
|
+
`r.push(((b[i]^${key})|((b[i+1]^${key})<<8)|((b[i+2]^${key})<<16)|((b[i+3]^${key})<<24))|0);` +
|
|
44
|
+
`}return r;}` +
|
|
45
|
+
`var ${ancName}=${decoderName}('${b64}');` +
|
|
46
|
+
hshSource(hshName));
|
|
47
|
+
}
|
package/jsobf-config.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nodeJsObfOptions = nodeJsObfOptions;
|
|
4
|
+
function nodeJsObfOptions(rng, integrityOn = true) {
|
|
5
|
+
return {
|
|
6
|
+
compact: true,
|
|
7
|
+
controlFlowFlattening: true,
|
|
8
|
+
controlFlowFlatteningThreshold: 0.75,
|
|
9
|
+
deadCodeInjection: true,
|
|
10
|
+
deadCodeInjectionThreshold: 0.4,
|
|
11
|
+
debugProtection: false,
|
|
12
|
+
debugProtectionInterval: 0,
|
|
13
|
+
disableConsoleOutput: false,
|
|
14
|
+
identifierNamesGenerator: "mangled-shuffled",
|
|
15
|
+
log: false,
|
|
16
|
+
// numbersToExpressions injects __hsh(__anc) via MBA anchor; disable when
|
|
17
|
+
// integrity anchors are off so __hsh is never referenced in the output.
|
|
18
|
+
numbersToExpressions: integrityOn,
|
|
19
|
+
renameGlobals: false,
|
|
20
|
+
selfDefending: true,
|
|
21
|
+
simplify: true,
|
|
22
|
+
splitStrings: true,
|
|
23
|
+
splitStringsChunkLength: 5,
|
|
24
|
+
stringArray: true,
|
|
25
|
+
stringArrayCallsTransform: true,
|
|
26
|
+
stringArrayEncoding: ["rc4"],
|
|
27
|
+
stringArrayIndexShift: true,
|
|
28
|
+
stringArrayRotate: true,
|
|
29
|
+
stringArrayShuffle: true,
|
|
30
|
+
stringArrayWrappersCount: 5,
|
|
31
|
+
stringArrayWrappersChainedCalls: true,
|
|
32
|
+
stringArrayWrappersType: "function",
|
|
33
|
+
stringArrayThreshold: 0.85,
|
|
34
|
+
transformObjectKeys: true,
|
|
35
|
+
unicodeEscapeSequence: false,
|
|
36
|
+
seed: rng.int32(),
|
|
37
|
+
};
|
|
38
|
+
}
|