bla-agents 1.1.0 → 1.6.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/.agents/skills/blind-llm-agents/SKILL.md +20 -14
- package/DISCLAIMER.md +15 -7
- package/README.md +153 -198
- package/dist/bundler/index.d.ts +19 -2
- package/dist/bundler/index.d.ts.map +1 -1
- package/dist/bundler/index.js +47 -4
- package/dist/bundler/index.js.map +1 -1
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +123 -18
- package/dist/cli.js.map +1 -1
- package/dist/core/config.d.ts +54 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +160 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/coverage-contract.d.ts +97 -0
- package/dist/core/coverage-contract.d.ts.map +1 -0
- package/dist/core/coverage-contract.js +164 -0
- package/dist/core/coverage-contract.js.map +1 -0
- package/dist/core/debug-posture.d.ts +40 -0
- package/dist/core/debug-posture.d.ts.map +1 -0
- package/dist/core/debug-posture.js +197 -0
- package/dist/core/debug-posture.js.map +1 -0
- package/dist/core/detector.d.ts.map +1 -1
- package/dist/core/detector.js +58 -6
- package/dist/core/detector.js.map +1 -1
- package/dist/core/identifiers.d.ts +32 -0
- package/dist/core/identifiers.d.ts.map +1 -0
- package/dist/core/identifiers.js +263 -0
- package/dist/core/identifiers.js.map +1 -0
- package/dist/core/llm-directives.d.ts +12 -0
- package/dist/core/llm-directives.d.ts.map +1 -0
- package/dist/core/llm-directives.js +23 -0
- package/dist/core/llm-directives.js.map +1 -0
- package/dist/core/prng.d.ts +10 -1
- package/dist/core/prng.d.ts.map +1 -1
- package/dist/core/prng.js +27 -2
- package/dist/core/prng.js.map +1 -1
- package/dist/core/release-exposure.d.ts.map +1 -1
- package/dist/core/release-exposure.js +13 -2
- package/dist/core/release-exposure.js.map +1 -1
- package/dist/core/release-plan.d.ts +68 -0
- package/dist/core/release-plan.d.ts.map +1 -0
- package/dist/core/release-plan.js +244 -0
- package/dist/core/release-plan.js.map +1 -0
- package/dist/core/sarif.d.ts +53 -0
- package/dist/core/sarif.d.ts.map +1 -0
- package/dist/core/sarif.js +95 -0
- package/dist/core/sarif.js.map +1 -0
- package/dist/core/sqlite-runtime.d.ts +13 -0
- package/dist/core/sqlite-runtime.d.ts.map +1 -1
- package/dist/core/sqlite-runtime.js +38 -8
- package/dist/core/sqlite-runtime.js.map +1 -1
- package/dist/core/types.d.ts +7 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/engine/auditor.d.ts.map +1 -1
- package/dist/engine/auditor.js +380 -19
- package/dist/engine/auditor.js.map +1 -1
- package/dist/engine/benchmark.d.ts +1 -1
- package/dist/engine/benchmark.d.ts.map +1 -1
- package/dist/engine/benchmark.js +121 -41
- package/dist/engine/benchmark.js.map +1 -1
- package/dist/engine/defender.d.ts +1 -0
- package/dist/engine/defender.d.ts.map +1 -1
- package/dist/engine/defender.js +7 -1
- package/dist/engine/defender.js.map +1 -1
- package/dist/engine/harden.d.ts +61 -0
- package/dist/engine/harden.d.ts.map +1 -0
- package/dist/engine/harden.js +294 -0
- package/dist/engine/harden.js.map +1 -0
- package/dist/engine/protector.d.ts.map +1 -1
- package/dist/engine/protector.js +242 -26
- package/dist/engine/protector.js.map +1 -1
- package/dist/identity.d.ts +112 -2
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +112 -2
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +26 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +59 -152
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +466 -50
- package/dist/mcp/server.js.map +1 -1
- package/dist/setup/clients.d.ts +49 -1
- package/dist/setup/clients.d.ts.map +1 -1
- package/dist/setup/clients.js +1193 -20
- package/dist/setup/clients.js.map +1 -1
- package/dist/setup/doctor.d.ts +58 -14
- package/dist/setup/doctor.d.ts.map +1 -1
- package/dist/setup/doctor.js +316 -22
- package/dist/setup/doctor.js.map +1 -1
- package/dist/setup/setup.d.ts +77 -7
- package/dist/setup/setup.d.ts.map +1 -1
- package/dist/setup/setup.js +591 -62
- package/dist/setup/setup.js.map +1 -1
- package/dist/shields/chaff/chaff-generator.d.ts.map +1 -1
- package/dist/shields/chaff/chaff-generator.js +12 -3
- package/dist/shields/chaff/chaff-generator.js.map +1 -1
- package/dist/shields/deception/architecture-mirage.js +1 -1
- package/dist/shields/deception/architecture-mirage.js.map +1 -1
- package/dist/shields/deception/canary-honeypot.d.ts +33 -0
- package/dist/shields/deception/canary-honeypot.d.ts.map +1 -0
- package/dist/shields/deception/canary-honeypot.js +95 -0
- package/dist/shields/deception/canary-honeypot.js.map +1 -0
- package/dist/shields/deception/cognitive-armor-trigger.d.ts +43 -0
- package/dist/shields/deception/cognitive-armor-trigger.d.ts.map +1 -0
- package/dist/shields/deception/cognitive-armor-trigger.js +204 -0
- package/dist/shields/deception/cognitive-armor-trigger.js.map +1 -0
- package/dist/shields/deception/crash-trace-decoy.js +1 -1
- package/dist/shields/deception/crash-trace-decoy.js.map +1 -1
- package/dist/shields/deception/decompiler-illusion.js +1 -1
- package/dist/shields/deception/decompiler-illusion.js.map +1 -1
- package/dist/shields/deception/diff-noise-injector.js +1 -1
- package/dist/shields/deception/diff-noise-injector.js.map +1 -1
- package/dist/shields/deception/evidence-contract-poison.js +5 -5
- package/dist/shields/deception/evidence-contract-poison.js.map +1 -1
- package/dist/shields/deception/format-payloads.d.ts +21 -0
- package/dist/shields/deception/format-payloads.d.ts.map +1 -0
- package/dist/shields/deception/format-payloads.js +24 -0
- package/dist/shields/deception/format-payloads.js.map +1 -0
- package/dist/shields/deception/index.d.ts +10 -2
- package/dist/shields/deception/index.d.ts.map +1 -1
- package/dist/shields/deception/index.js +20 -3
- package/dist/shields/deception/index.js.map +1 -1
- package/dist/shields/deception/prompt-completion-poison.d.ts +3 -2
- package/dist/shields/deception/prompt-completion-poison.d.ts.map +1 -1
- package/dist/shields/deception/prompt-completion-poison.js +6 -3
- package/dist/shields/deception/prompt-completion-poison.js.map +1 -1
- package/dist/shields/deception/shadow-business-logic.js +4 -4
- package/dist/shields/deception/shadow-business-logic.js.map +1 -1
- package/dist/shields/deception/system-diagnostic-decoy.d.ts +36 -0
- package/dist/shields/deception/system-diagnostic-decoy.d.ts.map +1 -0
- package/dist/shields/deception/system-diagnostic-decoy.js +101 -0
- package/dist/shields/deception/system-diagnostic-decoy.js.map +1 -0
- package/dist/shields/exhaustion/asar-header-amplifier.d.ts +12 -3
- package/dist/shields/exhaustion/asar-header-amplifier.d.ts.map +1 -1
- package/dist/shields/exhaustion/asar-header-amplifier.js +30 -8
- package/dist/shields/exhaustion/asar-header-amplifier.js.map +1 -1
- package/dist/shields/exhaustion/bpe-token-amplifier.js +2 -2
- package/dist/shields/exhaustion/bpe-token-amplifier.js.map +1 -1
- package/dist/shields/exhaustion/callgraph-labyrinth.js +2 -2
- package/dist/shields/exhaustion/callgraph-labyrinth.js.map +1 -1
- package/dist/shields/exhaustion/cdp-shape-budget-breaker.js +1 -1
- package/dist/shields/exhaustion/cdp-shape-budget-breaker.js.map +1 -1
- package/dist/shields/exhaustion/frida-capture-saturation.d.ts +10 -5
- package/dist/shields/exhaustion/frida-capture-saturation.d.ts.map +1 -1
- package/dist/shields/exhaustion/frida-capture-saturation.js +21 -8
- package/dist/shields/exhaustion/frida-capture-saturation.js.map +1 -1
- package/dist/shields/exhaustion/index.d.ts +2 -2
- package/dist/shields/exhaustion/index.d.ts.map +1 -1
- package/dist/shields/exhaustion/index.js +2 -2
- package/dist/shields/exhaustion/index.js.map +1 -1
- package/dist/shields/exhaustion/obligation-flooder.d.ts +23 -6
- package/dist/shields/exhaustion/obligation-flooder.d.ts.map +1 -1
- package/dist/shields/exhaustion/obligation-flooder.js +29 -12
- package/dist/shields/exhaustion/obligation-flooder.js.map +1 -1
- package/dist/shields/exhaustion/rea-budget-matrix.d.ts +8 -2
- package/dist/shields/exhaustion/rea-budget-matrix.d.ts.map +1 -1
- package/dist/shields/exhaustion/rea-budget-matrix.js +139 -16
- package/dist/shields/exhaustion/rea-budget-matrix.js.map +1 -1
- package/dist/shields/exhaustion/regex-worker-tarpit.d.ts.map +1 -1
- package/dist/shields/exhaustion/regex-worker-tarpit.js +2 -1
- package/dist/shields/exhaustion/regex-worker-tarpit.js.map +1 -1
- package/dist/shields/exhaustion/wasm-interface-exhaustion.d.ts +7 -4
- package/dist/shields/exhaustion/wasm-interface-exhaustion.d.ts.map +1 -1
- package/dist/shields/exhaustion/wasm-interface-exhaustion.js +16 -7
- package/dist/shields/exhaustion/wasm-interface-exhaustion.js.map +1 -1
- package/dist/shields/formats/android-dex-xref.d.ts +19 -0
- package/dist/shields/formats/android-dex-xref.d.ts.map +1 -0
- package/dist/shields/formats/android-dex-xref.js +427 -0
- package/dist/shields/formats/android-dex-xref.js.map +1 -0
- package/dist/shields/formats/android-manifest.d.ts +44 -0
- package/dist/shields/formats/android-manifest.d.ts.map +1 -0
- package/dist/shields/formats/android-manifest.js +240 -0
- package/dist/shields/formats/android-manifest.js.map +1 -0
- package/dist/shields/formats/android-r8.d.ts +44 -0
- package/dist/shields/formats/android-r8.d.ts.map +1 -0
- package/dist/shields/formats/android-r8.js +246 -0
- package/dist/shields/formats/android-r8.js.map +1 -0
- package/dist/shields/formats/apple-plist.d.ts +15 -0
- package/dist/shields/formats/apple-plist.d.ts.map +1 -1
- package/dist/shields/formats/apple-plist.js +59 -1
- package/dist/shields/formats/apple-plist.js.map +1 -1
- package/dist/shields/formats/archive.d.ts +27 -6
- package/dist/shields/formats/archive.d.ts.map +1 -1
- package/dist/shields/formats/archive.js +247 -45
- package/dist/shields/formats/archive.js.map +1 -1
- package/dist/shields/formats/asar.d.ts +50 -10
- package/dist/shields/formats/asar.d.ts.map +1 -1
- package/dist/shields/formats/asar.js +265 -39
- package/dist/shields/formats/asar.js.map +1 -1
- package/dist/shields/formats/bun-standalone.d.ts +78 -0
- package/dist/shields/formats/bun-standalone.d.ts.map +1 -0
- package/dist/shields/formats/bun-standalone.js +272 -0
- package/dist/shields/formats/bun-standalone.js.map +1 -0
- package/dist/shields/formats/dotnet-managed.d.ts +9 -0
- package/dist/shields/formats/dotnet-managed.d.ts.map +1 -1
- package/dist/shields/formats/dotnet-managed.js +26 -1
- package/dist/shields/formats/dotnet-managed.js.map +1 -1
- package/dist/shields/formats/electron-fuses.d.ts +63 -8
- package/dist/shields/formats/electron-fuses.d.ts.map +1 -1
- package/dist/shields/formats/electron-fuses.js +297 -65
- package/dist/shields/formats/electron-fuses.js.map +1 -1
- package/dist/shields/formats/erlang-beam.d.ts +34 -0
- package/dist/shields/formats/erlang-beam.d.ts.map +1 -0
- package/dist/shields/formats/erlang-beam.js +319 -0
- package/dist/shields/formats/erlang-beam.js.map +1 -0
- package/dist/shields/formats/evm-bytecode.d.ts +28 -0
- package/dist/shields/formats/evm-bytecode.d.ts.map +1 -0
- package/dist/shields/formats/evm-bytecode.js +124 -0
- package/dist/shields/formats/evm-bytecode.js.map +1 -0
- package/dist/shields/formats/go-flutter-exhaustion.d.ts +55 -0
- package/dist/shields/formats/go-flutter-exhaustion.d.ts.map +1 -0
- package/dist/shields/formats/go-flutter-exhaustion.js +346 -0
- package/dist/shields/formats/go-flutter-exhaustion.js.map +1 -0
- package/dist/shields/formats/gpu-ptx.d.ts +40 -0
- package/dist/shields/formats/gpu-ptx.d.ts.map +1 -0
- package/dist/shields/formats/gpu-ptx.js +62 -0
- package/dist/shields/formats/gpu-ptx.js.map +1 -0
- package/dist/shields/formats/headers.d.ts.map +1 -1
- package/dist/shields/formats/headers.js +54 -14
- package/dist/shields/formats/headers.js.map +1 -1
- package/dist/shields/formats/jvm-bytecode.d.ts +20 -0
- package/dist/shields/formats/jvm-bytecode.d.ts.map +1 -0
- package/dist/shields/formats/jvm-bytecode.js +134 -0
- package/dist/shields/formats/jvm-bytecode.js.map +1 -0
- package/dist/shields/formats/mobile-game.d.ts +4 -0
- package/dist/shields/formats/mobile-game.d.ts.map +1 -1
- package/dist/shields/formats/mobile-game.js +16 -0
- package/dist/shields/formats/mobile-game.js.map +1 -1
- package/dist/shields/formats/model.d.ts +22 -8
- package/dist/shields/formats/model.d.ts.map +1 -1
- package/dist/shields/formats/model.js +231 -59
- package/dist/shields/formats/model.js.map +1 -1
- package/dist/shields/formats/native.d.ts +74 -11
- package/dist/shields/formats/native.d.ts.map +1 -1
- package/dist/shields/formats/native.js +557 -109
- package/dist/shields/formats/native.js.map +1 -1
- package/dist/shields/formats/pcap-capture.d.ts +27 -0
- package/dist/shields/formats/pcap-capture.d.ts.map +1 -0
- package/dist/shields/formats/pcap-capture.js +201 -0
- package/dist/shields/formats/pcap-capture.js.map +1 -0
- package/dist/shields/formats/python.d.ts +22 -1
- package/dist/shields/formats/python.d.ts.map +1 -1
- package/dist/shields/formats/python.js +568 -27
- package/dist/shields/formats/python.js.map +1 -1
- package/dist/shields/formats/sourcemap.d.ts.map +1 -1
- package/dist/shields/formats/sourcemap.js +2 -0
- package/dist/shields/formats/sourcemap.js.map +1 -1
- package/dist/shields/formats/sqlite.d.ts +87 -15
- package/dist/shields/formats/sqlite.d.ts.map +1 -1
- package/dist/shields/formats/sqlite.js +377 -42
- package/dist/shields/formats/sqlite.js.map +1 -1
- package/dist/shields/formats/wasm.d.ts +6 -0
- package/dist/shields/formats/wasm.d.ts.map +1 -1
- package/dist/shields/formats/wasm.js +118 -3
- package/dist/shields/formats/wasm.js.map +1 -1
- package/dist/shields/javascript/ast-shield.d.ts +26 -5
- package/dist/shields/javascript/ast-shield.d.ts.map +1 -1
- package/dist/shields/javascript/ast-shield.js +748 -104
- package/dist/shields/javascript/ast-shield.js.map +1 -1
- package/dist/shields/runtime/memory-diff-shield.d.ts +56 -0
- package/dist/shields/runtime/memory-diff-shield.d.ts.map +1 -0
- package/dist/shields/runtime/memory-diff-shield.js +170 -0
- package/dist/shields/runtime/memory-diff-shield.js.map +1 -0
- package/dist/shields/runtime/runtime-shield.d.ts +12 -16
- package/dist/shields/runtime/runtime-shield.d.ts.map +1 -1
- package/dist/shields/runtime/runtime-shield.js +30 -24
- package/dist/shields/runtime/runtime-shield.js.map +1 -1
- package/dist/shields/tarpit/opaque-predicates.d.ts.map +1 -1
- package/dist/shields/tarpit/opaque-predicates.js +3 -3
- package/dist/shields/tarpit/opaque-predicates.js.map +1 -1
- package/dist/shields/tarpit/tarpit-generator.d.ts +9 -4
- package/dist/shields/tarpit/tarpit-generator.d.ts.map +1 -1
- package/dist/shields/tarpit/tarpit-generator.js +30 -23
- package/dist/shields/tarpit/tarpit-generator.js.map +1 -1
- package/package.json +13 -6
|
@@ -1,43 +1,560 @@
|
|
|
1
1
|
import { SeededPrng } from "../../core/prng.js";
|
|
2
2
|
import { generateChaffPayloads } from "../chaff/chaff-generator.js";
|
|
3
|
+
import { buildCanaryAndDiagnosticPayloads } from "../deception/format-payloads.js";
|
|
3
4
|
import { generatePythonTarpit } from "../tarpit/tarpit-generator.js";
|
|
5
|
+
export class InvalidPythonSourceError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "InvalidPythonSourceError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const VALID_STRING_PREFIXES = new Set([
|
|
12
|
+
"",
|
|
13
|
+
"r",
|
|
14
|
+
"u",
|
|
15
|
+
"f",
|
|
16
|
+
"b",
|
|
17
|
+
"fr",
|
|
18
|
+
"rf",
|
|
19
|
+
"br",
|
|
20
|
+
"rb",
|
|
21
|
+
]);
|
|
22
|
+
function matchPythonStringLiteralStart(source, offset) {
|
|
23
|
+
if (offset > 0 && /[A-Za-z0-9_]/.test(source[offset - 1])) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const slice = source.slice(offset, offset + 5);
|
|
27
|
+
const m = /^([A-Za-z]{0,2})("""|'''|"|')/.exec(slice);
|
|
28
|
+
if (!m)
|
|
29
|
+
return null;
|
|
30
|
+
const rawPrefix = m[1] ?? "";
|
|
31
|
+
const lowerPrefix = rawPrefix.toLowerCase();
|
|
32
|
+
if (!VALID_STRING_PREFIXES.has(lowerPrefix)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const quote = (source.startsWith('"""', offset + rawPrefix.length)
|
|
36
|
+
? '"""'
|
|
37
|
+
: source.startsWith("'''", offset + rawPrefix.length)
|
|
38
|
+
? "'''"
|
|
39
|
+
: source[offset + rawPrefix.length]);
|
|
40
|
+
const isDocstringEligible = lowerPrefix === "" || lowerPrefix === "r" || lowerPrefix === "u";
|
|
41
|
+
return {
|
|
42
|
+
prefix: rawPrefix,
|
|
43
|
+
quote,
|
|
44
|
+
isDocstringEligible,
|
|
45
|
+
contentStart: offset + rawPrefix.length + quote.length,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function scanPythonStringLiteralEnd(source, startOffset) {
|
|
49
|
+
const match = matchPythonStringLiteralStart(source, startOffset);
|
|
50
|
+
if (!match) {
|
|
51
|
+
throw new InvalidPythonSourceError(`Expected Python string literal at offset ${startOffset}`);
|
|
52
|
+
}
|
|
53
|
+
const { quote, contentStart, isDocstringEligible } = match;
|
|
54
|
+
let i = contentStart;
|
|
55
|
+
if (quote.length === 3) {
|
|
56
|
+
while (i < source.length) {
|
|
57
|
+
if (source.startsWith(quote, i)) {
|
|
58
|
+
return { endOffset: i + 3, isDocstringEligible };
|
|
59
|
+
}
|
|
60
|
+
if (source[i] === "\\") {
|
|
61
|
+
if (i + 1 >= source.length) {
|
|
62
|
+
throw new InvalidPythonSourceError("Unclosed triple-quoted string literal in Python source");
|
|
63
|
+
}
|
|
64
|
+
if (source[i + 1] === "\r" && source[i + 2] === "\n") {
|
|
65
|
+
i += 3;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
i += 2;
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
i += 1;
|
|
73
|
+
}
|
|
74
|
+
throw new InvalidPythonSourceError("Unclosed triple-quoted string literal in Python source");
|
|
75
|
+
}
|
|
76
|
+
while (i < source.length) {
|
|
77
|
+
const ch = source[i];
|
|
78
|
+
if (ch === quote) {
|
|
79
|
+
return { endOffset: i + 1, isDocstringEligible };
|
|
80
|
+
}
|
|
81
|
+
if (ch === "\n" || ch === "\r") {
|
|
82
|
+
throw new InvalidPythonSourceError("Unclosed single-line string literal in Python source");
|
|
83
|
+
}
|
|
84
|
+
if (ch === "\\") {
|
|
85
|
+
if (i + 1 >= source.length) {
|
|
86
|
+
throw new InvalidPythonSourceError("Unclosed string literal ending with backslash in Python source");
|
|
87
|
+
}
|
|
88
|
+
if (source[i + 1] === "\r" && source[i + 2] === "\n") {
|
|
89
|
+
i += 3;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
i += 2;
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
i += 1;
|
|
97
|
+
}
|
|
98
|
+
throw new InvalidPythonSourceError("Unclosed single-line string literal in Python source");
|
|
99
|
+
}
|
|
100
|
+
function skipHorizontalWhitespace(source, offset) {
|
|
101
|
+
let i = offset;
|
|
102
|
+
while (i < source.length) {
|
|
103
|
+
const ch = source[i];
|
|
104
|
+
if (ch === " " || ch === "\t" || ch === "\f") {
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return i;
|
|
112
|
+
}
|
|
113
|
+
function skipHorizontalAndBackslashNewlines(source, offset) {
|
|
114
|
+
let i = offset;
|
|
115
|
+
while (i < source.length) {
|
|
116
|
+
i = skipHorizontalWhitespace(source, i);
|
|
117
|
+
if (source[i] === "\\") {
|
|
118
|
+
if (source[i + 1] === "\n") {
|
|
119
|
+
i += 2;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (source[i + 1] === "\r" && source[i + 2] === "\n") {
|
|
123
|
+
i += 3;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
throw new InvalidPythonSourceError(`Invalid backslash line continuation at offset ${i}`);
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
return i;
|
|
131
|
+
}
|
|
132
|
+
function skipTriviaAndComments(source, offset) {
|
|
133
|
+
let i = offset;
|
|
134
|
+
while (i < source.length) {
|
|
135
|
+
const ch = source[i];
|
|
136
|
+
if (ch === " " || ch === "\t" || ch === "\f" || ch === "\r" || ch === "\n") {
|
|
137
|
+
i++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (ch === "#") {
|
|
141
|
+
while (i < source.length && source[i] !== "\n" && source[i] !== "\r") {
|
|
142
|
+
i++;
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
return i;
|
|
149
|
+
}
|
|
150
|
+
function consumeOptionalEndOfLineAfterStatement(source, offset) {
|
|
151
|
+
let i = skipHorizontalWhitespace(source, offset);
|
|
152
|
+
if (source[i] === "#") {
|
|
153
|
+
while (i < source.length && source[i] !== "\n" && source[i] !== "\r") {
|
|
154
|
+
i++;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (source[i] === "\r" && source[i + 1] === "\n") {
|
|
158
|
+
return i + 2;
|
|
159
|
+
}
|
|
160
|
+
if (source[i] === "\n" || source[i] === "\r") {
|
|
161
|
+
return i + 1;
|
|
162
|
+
}
|
|
163
|
+
return offset;
|
|
164
|
+
}
|
|
165
|
+
function tryConsumeModuleDocstring(source, startOffset) {
|
|
166
|
+
let cur = startOffset;
|
|
167
|
+
let parenDepth = 0;
|
|
168
|
+
while (cur < source.length && source[cur] === "(") {
|
|
169
|
+
parenDepth++;
|
|
170
|
+
cur++;
|
|
171
|
+
cur =
|
|
172
|
+
parenDepth > 0
|
|
173
|
+
? skipTriviaAndComments(source, cur)
|
|
174
|
+
: skipHorizontalAndBackslashNewlines(source, cur);
|
|
175
|
+
}
|
|
176
|
+
const firstLit = matchPythonStringLiteralStart(source, cur);
|
|
177
|
+
if (!firstLit || !firstLit.isDocstringEligible) {
|
|
178
|
+
return { matched: false, nextOffset: startOffset };
|
|
179
|
+
}
|
|
180
|
+
let sawString = false;
|
|
181
|
+
while (cur < source.length) {
|
|
182
|
+
const litMatch = matchPythonStringLiteralStart(source, cur);
|
|
183
|
+
if (!litMatch)
|
|
184
|
+
break;
|
|
185
|
+
if (!litMatch.isDocstringEligible) {
|
|
186
|
+
return { matched: false, nextOffset: startOffset };
|
|
187
|
+
}
|
|
188
|
+
const scanned = scanPythonStringLiteralEnd(source, cur);
|
|
189
|
+
sawString = true;
|
|
190
|
+
cur = scanned.endOffset;
|
|
191
|
+
cur =
|
|
192
|
+
parenDepth > 0
|
|
193
|
+
? skipTriviaAndComments(source, cur)
|
|
194
|
+
: skipHorizontalAndBackslashNewlines(source, cur);
|
|
195
|
+
}
|
|
196
|
+
if (!sawString) {
|
|
197
|
+
return { matched: false, nextOffset: startOffset };
|
|
198
|
+
}
|
|
199
|
+
while (parenDepth > 0) {
|
|
200
|
+
cur = skipTriviaAndComments(source, cur);
|
|
201
|
+
if (cur >= source.length) {
|
|
202
|
+
throw new InvalidPythonSourceError("Unclosed parenthesis in Python module docstring");
|
|
203
|
+
}
|
|
204
|
+
if (source[cur] !== ")") {
|
|
205
|
+
return { matched: false, nextOffset: startOffset };
|
|
206
|
+
}
|
|
207
|
+
parenDepth--;
|
|
208
|
+
cur++;
|
|
209
|
+
}
|
|
210
|
+
cur = skipHorizontalAndBackslashNewlines(source, cur);
|
|
211
|
+
if (source[cur] === "#") {
|
|
212
|
+
while (cur < source.length && source[cur] !== "\n" && source[cur] !== "\r") {
|
|
213
|
+
cur++;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (cur >= source.length) {
|
|
217
|
+
return { matched: true, nextOffset: cur };
|
|
218
|
+
}
|
|
219
|
+
if (source[cur] === "\r" && source[cur + 1] === "\n") {
|
|
220
|
+
return { matched: true, nextOffset: cur + 2 };
|
|
221
|
+
}
|
|
222
|
+
if (source[cur] === "\n" || source[cur] === "\r") {
|
|
223
|
+
return { matched: true, nextOffset: cur + 1 };
|
|
224
|
+
}
|
|
225
|
+
if (source[cur] === ";") {
|
|
226
|
+
const afterSemi = cur + 1;
|
|
227
|
+
const afterLine = consumeOptionalEndOfLineAfterStatement(source, afterSemi);
|
|
228
|
+
return { matched: true, nextOffset: afterLine };
|
|
229
|
+
}
|
|
230
|
+
// Compound expression starting with a string literal (e.g. `"a" + "b"`), not a docstring
|
|
231
|
+
return { matched: false, nextOffset: startOffset };
|
|
232
|
+
}
|
|
233
|
+
function matchKeywordAt(source, offset, keyword) {
|
|
234
|
+
if (!source.startsWith(keyword, offset))
|
|
235
|
+
return false;
|
|
236
|
+
if (offset > 0 && /[A-Za-z0-9_]/.test(source[offset - 1]))
|
|
237
|
+
return false;
|
|
238
|
+
const after = source[offset + keyword.length];
|
|
239
|
+
if (after !== undefined && /[A-Za-z0-9_]/.test(after))
|
|
240
|
+
return false;
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Lexically analyzes a Python module to find the exact safe insertion offset
|
|
245
|
+
* strictly AFTER:
|
|
246
|
+
* 1. UTF-8 BOM, shebang (`#!`), and PEP 263 encoding comments
|
|
247
|
+
* 2. The optional module docstring (preserving `module.__doc__`)
|
|
248
|
+
* 3. All `from __future__ import ...` statements (single-line, parenthesized multi-line, or backslash-continued)
|
|
249
|
+
*/
|
|
250
|
+
export function findPythonPrologueInsertionOffset(source) {
|
|
251
|
+
let pos = 0;
|
|
252
|
+
if (source.startsWith("\uFEFF")) {
|
|
253
|
+
pos = 1;
|
|
254
|
+
}
|
|
255
|
+
// Advance past leading blank lines and `#` comments (including shebang & PEP 263 encoding declaration)
|
|
256
|
+
pos = skipTriviaAndComments(source, pos);
|
|
257
|
+
let insertOffset = pos;
|
|
258
|
+
let hasDocstring = false;
|
|
259
|
+
const futureImports = [];
|
|
260
|
+
// 1. Check for optional module docstring as the very first statement
|
|
261
|
+
if (pos < source.length) {
|
|
262
|
+
const docAttempt = tryConsumeModuleDocstring(source, pos);
|
|
263
|
+
if (docAttempt.matched) {
|
|
264
|
+
hasDocstring = true;
|
|
265
|
+
pos = docAttempt.nextOffset;
|
|
266
|
+
insertOffset = pos;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// 2. Consume zero or more `from __future__ import ...` statements
|
|
270
|
+
while (pos < source.length) {
|
|
271
|
+
const nextStmtStart = skipTriviaAndComments(source, pos);
|
|
272
|
+
if (nextStmtStart >= source.length) {
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
if (!matchKeywordAt(source, nextStmtStart, "from")) {
|
|
276
|
+
pos = nextStmtStart;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
let cur = skipHorizontalAndBackslashNewlines(source, nextStmtStart + 4);
|
|
280
|
+
if (!matchKeywordAt(source, cur, "__future__")) {
|
|
281
|
+
// Normal `from <pkg> import ...` statement; prologue is complete
|
|
282
|
+
pos = nextStmtStart;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
cur = skipHorizontalAndBackslashNewlines(source, cur + "__future__".length);
|
|
286
|
+
if (!matchKeywordAt(source, cur, "import")) {
|
|
287
|
+
throw new InvalidPythonSourceError("Malformed 'from __future__' statement: expected 'import'");
|
|
288
|
+
}
|
|
289
|
+
cur += "import".length;
|
|
290
|
+
let parenDepth = 0;
|
|
291
|
+
let sawIdentifier = false;
|
|
292
|
+
let expectingCommaOrEnd = false;
|
|
293
|
+
let expectingAliasIdentifier = false;
|
|
294
|
+
let closedParen = false;
|
|
295
|
+
while (cur < source.length) {
|
|
296
|
+
cur = skipHorizontalWhitespace(source, cur);
|
|
297
|
+
if (cur >= source.length)
|
|
298
|
+
break;
|
|
299
|
+
const ch = source[cur];
|
|
300
|
+
if (ch === "\\") {
|
|
301
|
+
if (source[cur + 1] === "\n") {
|
|
302
|
+
cur += 2;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (source[cur + 1] === "\r" && source[cur + 2] === "\n") {
|
|
306
|
+
cur += 3;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
throw new InvalidPythonSourceError("Invalid backslash continuation in from __future__ import");
|
|
310
|
+
}
|
|
311
|
+
if (ch === "#") {
|
|
312
|
+
while (cur < source.length && source[cur] !== "\n" && source[cur] !== "\r") {
|
|
313
|
+
cur++;
|
|
314
|
+
}
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (ch === "\r" || ch === "\n") {
|
|
318
|
+
if (source[cur] === "\r" && source[cur + 1] === "\n") {
|
|
319
|
+
cur += 2;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
cur += 1;
|
|
323
|
+
}
|
|
324
|
+
if (parenDepth === 0) {
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (ch === ";") {
|
|
330
|
+
if (parenDepth > 0 || expectingAliasIdentifier || !sawIdentifier) {
|
|
331
|
+
throw new InvalidPythonSourceError("Invalid semicolon in from __future__ import");
|
|
332
|
+
}
|
|
333
|
+
cur += 1;
|
|
334
|
+
cur = consumeOptionalEndOfLineAfterStatement(source, cur);
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
if (closedParen) {
|
|
338
|
+
throw new InvalidPythonSourceError(`Unexpected token '${ch}' after closing ')' in from __future__ import`);
|
|
339
|
+
}
|
|
340
|
+
if (ch === "(") {
|
|
341
|
+
if (parenDepth > 0 || sawIdentifier) {
|
|
342
|
+
throw new InvalidPythonSourceError("Unexpected '(' in from __future__ import");
|
|
343
|
+
}
|
|
344
|
+
parenDepth = 1;
|
|
345
|
+
cur += 1;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (ch === ")") {
|
|
349
|
+
if (parenDepth <= 0 || expectingAliasIdentifier || !sawIdentifier) {
|
|
350
|
+
throw new InvalidPythonSourceError("Unexpected ')' in from __future__ import");
|
|
351
|
+
}
|
|
352
|
+
parenDepth = 0;
|
|
353
|
+
closedParen = true;
|
|
354
|
+
cur += 1;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (ch === ",") {
|
|
358
|
+
if (!expectingCommaOrEnd || expectingAliasIdentifier) {
|
|
359
|
+
throw new InvalidPythonSourceError("Unexpected ',' in from __future__ import");
|
|
360
|
+
}
|
|
361
|
+
expectingCommaOrEnd = false;
|
|
362
|
+
cur += 1;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (ch === "*") {
|
|
366
|
+
throw new InvalidPythonSourceError("Wildcard import '*' is not valid in from __future__ import");
|
|
367
|
+
}
|
|
368
|
+
if (/[A-Za-z_]/.test(ch)) {
|
|
369
|
+
const idMatch = /^[A-Za-z_][A-Za-z0-9_]*/.exec(source.slice(cur));
|
|
370
|
+
const ident = idMatch[0];
|
|
371
|
+
cur += ident.length;
|
|
372
|
+
if (expectingAliasIdentifier) {
|
|
373
|
+
expectingAliasIdentifier = false;
|
|
374
|
+
expectingCommaOrEnd = true;
|
|
375
|
+
}
|
|
376
|
+
else if (expectingCommaOrEnd && ident === "as") {
|
|
377
|
+
expectingAliasIdentifier = true;
|
|
378
|
+
expectingCommaOrEnd = false;
|
|
379
|
+
}
|
|
380
|
+
else if (!expectingCommaOrEnd) {
|
|
381
|
+
sawIdentifier = true;
|
|
382
|
+
expectingCommaOrEnd = true;
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
throw new InvalidPythonSourceError(`Unexpected identifier '${ident}' in from __future__ import`);
|
|
386
|
+
}
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
throw new InvalidPythonSourceError(`Unexpected character '${ch}' in from __future__ import`);
|
|
390
|
+
}
|
|
391
|
+
if (parenDepth !== 0) {
|
|
392
|
+
throw new InvalidPythonSourceError("Unclosed '(' in from __future__ import statement");
|
|
393
|
+
}
|
|
394
|
+
if (!sawIdentifier || expectingAliasIdentifier) {
|
|
395
|
+
throw new InvalidPythonSourceError("Incomplete from __future__ import statement");
|
|
396
|
+
}
|
|
397
|
+
futureImports.push(source.slice(nextStmtStart, cur).trim());
|
|
398
|
+
pos = cur;
|
|
399
|
+
insertOffset = cur;
|
|
400
|
+
}
|
|
401
|
+
// 3. Scan remainder of file to reject unclosed string literals and misplaced `from __future__ import`
|
|
402
|
+
let scanPos = pos;
|
|
403
|
+
let atStatementStart = true;
|
|
404
|
+
let bracketDepth = 0;
|
|
405
|
+
while (scanPos < source.length) {
|
|
406
|
+
const ch = source[scanPos];
|
|
407
|
+
if (ch === " " || ch === "\t" || ch === "\f") {
|
|
408
|
+
scanPos++;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (ch === "#") {
|
|
412
|
+
while (scanPos < source.length &&
|
|
413
|
+
source[scanPos] !== "\n" &&
|
|
414
|
+
source[scanPos] !== "\r") {
|
|
415
|
+
scanPos++;
|
|
416
|
+
}
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (ch === "\n" || ch === "\r") {
|
|
420
|
+
if (ch === "\r" && source[scanPos + 1] === "\n") {
|
|
421
|
+
scanPos += 2;
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
scanPos += 1;
|
|
425
|
+
}
|
|
426
|
+
if (bracketDepth === 0) {
|
|
427
|
+
atStatementStart = true;
|
|
428
|
+
}
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (ch === ";") {
|
|
432
|
+
scanPos++;
|
|
433
|
+
if (bracketDepth === 0) {
|
|
434
|
+
atStatementStart = true;
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (ch === "\\") {
|
|
439
|
+
if (source[scanPos + 1] === "\n") {
|
|
440
|
+
scanPos += 2;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (source[scanPos + 1] === "\r" && source[scanPos + 2] === "\n") {
|
|
444
|
+
scanPos += 3;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
scanPos++;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
const strStart = matchPythonStringLiteralStart(source, scanPos);
|
|
451
|
+
if (strStart) {
|
|
452
|
+
const scanned = scanPythonStringLiteralEnd(source, scanPos);
|
|
453
|
+
scanPos = scanned.endOffset;
|
|
454
|
+
atStatementStart = false;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
458
|
+
bracketDepth++;
|
|
459
|
+
scanPos++;
|
|
460
|
+
atStatementStart = false;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
464
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
465
|
+
scanPos++;
|
|
466
|
+
atStatementStart = false;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (atStatementStart && bracketDepth === 0 && matchKeywordAt(source, scanPos, "from")) {
|
|
470
|
+
const afterFrom = skipHorizontalAndBackslashNewlines(source, scanPos + 4);
|
|
471
|
+
if (matchKeywordAt(source, afterFrom, "__future__")) {
|
|
472
|
+
throw new InvalidPythonSourceError("from __future__ imports must occur at the beginning of the file");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
atStatementStart = false;
|
|
476
|
+
if (/[A-Za-z0-9_]/.test(ch)) {
|
|
477
|
+
while (scanPos < source.length && /[A-Za-z0-9_]/.test(source[scanPos])) {
|
|
478
|
+
scanPos++;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
scanPos++;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
insertOffset,
|
|
487
|
+
hasDocstring,
|
|
488
|
+
futureImports,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function pythonReprString(value) {
|
|
492
|
+
return JSON.stringify(value);
|
|
493
|
+
}
|
|
494
|
+
function pythonStringTuple(values) {
|
|
495
|
+
if (values.length === 1) {
|
|
496
|
+
return `(${pythonReprString(values[0])},)`;
|
|
497
|
+
}
|
|
498
|
+
return `(${values.map(pythonReprString).join(", ")})`;
|
|
499
|
+
}
|
|
500
|
+
function pythonDiagnosticTuple(entries) {
|
|
501
|
+
const items = entries.map((d) => `{${[
|
|
502
|
+
`"subsystem": ${pythonReprString(d.subsystem)}`,
|
|
503
|
+
`"errorClass": ${pythonReprString(d.errorClass)}`,
|
|
504
|
+
`"systemCode": ${pythonReprString(d.systemCode)}`,
|
|
505
|
+
`"diagnosticMessage": ${pythonReprString(d.diagnosticMessage)}`,
|
|
506
|
+
`"remediationRabbitHole": ${pythonReprString(d.remediationRabbitHole)}`,
|
|
507
|
+
].join(", ")}}`);
|
|
508
|
+
if (items.length === 1)
|
|
509
|
+
return `(${items[0]},)`;
|
|
510
|
+
return `(${items.join(", ")})`;
|
|
511
|
+
}
|
|
4
512
|
/**
|
|
5
513
|
* Protects a Python `.py` source file by injecting an interconnected Python
|
|
6
514
|
* Algorithmic Tarpit (`generatePythonTarpit`), opaque predicates, and
|
|
7
|
-
* honey-docstrings while preserving shebangs
|
|
515
|
+
* honey-docstrings while preserving shebangs, PEP 263 encoding headers,
|
|
516
|
+
* module docstrings (`__doc__`), and complete `from __future__` imports.
|
|
8
517
|
*/
|
|
9
518
|
export function protectPythonSource(source, options = {}) {
|
|
10
519
|
const intensity = options.intensity ?? "balanced";
|
|
11
520
|
const prng = new SeededPrng(options.seed ?? "bla-python-shield-v1");
|
|
12
521
|
const chaffPayloads = generateChaffPayloads(prng, intensity);
|
|
13
522
|
const tarpit = generatePythonTarpit(prng, intensity, chaffPayloads);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
523
|
+
const deception = buildCanaryAndDiagnosticPayloads(prng, intensity);
|
|
524
|
+
const eol = source.includes("\r\n") ? "\r\n" : "\n";
|
|
525
|
+
const prologue = findPythonPrologueInsertionOffset(source);
|
|
526
|
+
const headRaw = source.slice(0, prologue.insertOffset);
|
|
527
|
+
const tailRaw = source.slice(prologue.insertOffset);
|
|
528
|
+
const head = headRaw.replace(/(?:\r?\n)+$/, "");
|
|
529
|
+
const tail = tailRaw.replace(/^(?:\r?\n)+/, "").replace(/(?:\r?\n)+$/, "");
|
|
530
|
+
const tarpitNormalized = eol === "\r\n"
|
|
531
|
+
? tarpit.code.replace(/\r?\n/g, "\r\n").replace(/(?:\r\n)+$/, "")
|
|
532
|
+
: tarpit.code.replace(/\n+$/, "");
|
|
533
|
+
// Inert canary + diagnostic + cognitive-armor decoys below prologue/tarpit. Data only — never fetched.
|
|
534
|
+
const cognitiveArmorStrings = deception.cognitiveArmor.triggersInjected.map((t) => `${t.coupledAnchorSymbol}::${t.triggerPayload.replace(/\r?\n/g, " ")}`);
|
|
535
|
+
const deceptionBlock = [
|
|
536
|
+
"# --- canary credential / transport-redaction honeypot (inert) ---",
|
|
537
|
+
`CANARY_TOKENS = ${pythonStringTuple(deception.canary.syntheticCanaryTokens)}`,
|
|
538
|
+
`AMBIGUOUS_TRANSPORT = ${pythonStringTuple(deception.canary.ambiguousCredentialUrls)}`,
|
|
539
|
+
`CONFLICTING_QUERY = ${pythonStringTuple(deception.canary.conflictingQueryUrls)}`,
|
|
540
|
+
"# --- system-diagnostic rabbit holes & cognitive-armor interlocks (inert) ---",
|
|
541
|
+
`SYSTEM_DIAGNOSTICS = ${pythonDiagnosticTuple(deception.diagnostics.diagnosticsInjected)}`,
|
|
542
|
+
`ALIGNMENT_EVAL_CANARY_GUID = ${pythonReprString(deception.cognitiveArmor.canaryGuid)}`,
|
|
543
|
+
`COGNITIVE_ARMOR_CORPUS = ${pythonStringTuple(cognitiveArmorStrings)}`,
|
|
544
|
+
"def probe_canary_honeypot(idx=0):",
|
|
545
|
+
" _i = (idx & 0x7FFFFFFF) % len(CANARY_TOKENS)",
|
|
546
|
+
" return AMBIGUOUS_TRANSPORT[_i]",
|
|
547
|
+
].join(eol);
|
|
548
|
+
const combined = [head, tarpitNormalized, deceptionBlock, tail]
|
|
34
549
|
.filter((part) => part.length > 0)
|
|
35
|
-
.join(
|
|
550
|
+
.join(eol + eol);
|
|
36
551
|
return {
|
|
37
|
-
code: combined +
|
|
552
|
+
code: combined + eol,
|
|
38
553
|
decoyFunctionsInjected: tarpit.decoyFunctionNames.length,
|
|
39
554
|
crossReferenceEdges: tarpit.crossReferenceEdges,
|
|
40
555
|
chaffPayloadsEmbedded: chaffPayloads.length,
|
|
556
|
+
canaryTokensEmbedded: deception.canary.syntheticCanaryTokens.length,
|
|
557
|
+
diagnosticsEmbedded: deception.diagnostics.diagnosticsInjected.length,
|
|
41
558
|
};
|
|
42
559
|
}
|
|
43
560
|
/**
|
|
@@ -63,7 +580,10 @@ export function inspectPythonPyc(bytes) {
|
|
|
63
580
|
magicNumber,
|
|
64
581
|
pep552Flags,
|
|
65
582
|
extractedStrings: matches.slice(0, 64),
|
|
66
|
-
blaProtected: textView.includes("BLA-SHIELD") ||
|
|
583
|
+
blaProtected: textView.includes("BLA-SHIELD") ||
|
|
584
|
+
textView.includes("Architecture Notice: Core business logic") ||
|
|
585
|
+
textView.includes("PROTECTED_CANARY_CREDENTIAL=") ||
|
|
586
|
+
textView.includes("DarwinProcessOwnershipInspectionError"),
|
|
67
587
|
};
|
|
68
588
|
}
|
|
69
589
|
/**
|
|
@@ -79,18 +599,39 @@ export function protectPythonPycBuffer(bytes, options = {}) {
|
|
|
79
599
|
const intensity = options.intensity ?? "balanced";
|
|
80
600
|
const prng = new SeededPrng(options.seed ?? "bla-pyc-shield-v1");
|
|
81
601
|
const chaffPayloads = generateChaffPayloads(prng, intensity);
|
|
82
|
-
|
|
602
|
+
const deception = buildCanaryAndDiagnosticPayloads(prng, intensity);
|
|
603
|
+
// Construct marshal-framed ASCII constant records (TYPE_ASCII = 0x61 + uint32LE length + utf8 bytes).
|
|
604
|
+
// Keep distinctive prefixes within REA's PYTHON_PYC_CONST_PREVIEW_LIMIT (80) so canary tokens
|
|
605
|
+
// and diagnostic errorClass/systemCode survive truncated previews.
|
|
83
606
|
const marshalRecords = [];
|
|
84
|
-
|
|
85
|
-
const strBytes = Buffer.from(
|
|
607
|
+
const pushAscii = (raw) => {
|
|
608
|
+
const strBytes = Buffer.from(raw, "utf8");
|
|
86
609
|
const header = Buffer.alloc(5);
|
|
87
610
|
header[0] = 0x61; // TYPE_ASCII
|
|
88
611
|
header.writeUInt32LE(strBytes.length, 1);
|
|
89
612
|
marshalRecords.push(header, strBytes);
|
|
613
|
+
};
|
|
614
|
+
for (const payload of chaffPayloads) {
|
|
615
|
+
pushAscii(payload.rawText);
|
|
616
|
+
}
|
|
617
|
+
for (const tok of deception.canary.syntheticCanaryTokens) {
|
|
618
|
+
pushAscii(`PROTECTED_CANARY_CREDENTIAL=${tok}`);
|
|
619
|
+
}
|
|
620
|
+
for (const url of deception.canary.ambiguousCredentialUrls) {
|
|
621
|
+
pushAscii(`TRANSPORT_ENDPOINT=${url}`);
|
|
622
|
+
}
|
|
623
|
+
for (const d of deception.diagnostics.diagnosticsInjected) {
|
|
624
|
+
// Leading errorClass+systemCode fit in the 80-byte CONST_PREVIEW window.
|
|
625
|
+
pushAscii(`[${d.errorClass}] (${d.systemCode}) ${d.diagnosticMessage}`);
|
|
626
|
+
}
|
|
627
|
+
for (const t of deception.cognitiveArmor.triggersInjected) {
|
|
628
|
+
pushAscii(`[${t.coupledAnchorSymbol}] ${t.triggerPayload.replace(/\r?\n/g, " ")}`);
|
|
90
629
|
}
|
|
91
630
|
return {
|
|
92
631
|
buffer: Buffer.concat([bytes, ...marshalRecords]),
|
|
93
632
|
chaffPayloadsEmbedded: chaffPayloads.length,
|
|
633
|
+
canaryTokensEmbedded: deception.canary.syntheticCanaryTokens.length,
|
|
634
|
+
diagnosticsEmbedded: deception.diagnostics.diagnosticsInjected.length,
|
|
94
635
|
};
|
|
95
636
|
}
|
|
96
637
|
//# sourceMappingURL=python.js.map
|