afterpack 0.0.11 → 0.0.14
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/dist/cli.js +251 -28
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
- package/readme.md +28 -2
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,239 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
//#region package.json
|
|
3
|
-
var version = "0.0.
|
|
3
|
+
var version = "0.0.14";
|
|
4
|
+
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/utils/format.ts
|
|
7
|
+
const isColorSupported = process.env.FORCE_COLOR !== "0" && !process.env.NO_COLOR && (process.env.FORCE_COLOR !== void 0 || process.stdout.isTTY);
|
|
8
|
+
function ansi(open, close) {
|
|
9
|
+
return (text) => isColorSupported ? `\x1b[${open}m${text}\x1b[${close}m` : text;
|
|
10
|
+
}
|
|
11
|
+
const bold = ansi("1", "22");
|
|
12
|
+
const dim = ansi("2", "22");
|
|
13
|
+
const red = ansi("31", "39");
|
|
14
|
+
const green = ansi("32", "39");
|
|
15
|
+
const yellow = ansi("33", "39");
|
|
16
|
+
const cyan = ansi("36", "39");
|
|
17
|
+
function severityColor(severity) {
|
|
18
|
+
switch (severity.toLowerCase()) {
|
|
19
|
+
case "critical": return red;
|
|
20
|
+
case "high": return red;
|
|
21
|
+
case "medium": return yellow;
|
|
22
|
+
case "low": return cyan;
|
|
23
|
+
default: return dim;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function header(version$1) {
|
|
27
|
+
return `\n ${bold(`AfterPack v${version$1}`)} — Security Analyzer\n`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/utils/sse.ts
|
|
32
|
+
async function* streamSSE(url, options) {
|
|
33
|
+
const response = await fetch(url, options);
|
|
34
|
+
if (!response.ok) throw new Error(`SSE request failed: ${response.status} ${response.statusText}`);
|
|
35
|
+
const body = response.body;
|
|
36
|
+
if (!body) throw new Error("SSE response has no body");
|
|
37
|
+
const decoder = new TextDecoder();
|
|
38
|
+
let buffer = "";
|
|
39
|
+
let currentEvent = "message";
|
|
40
|
+
let currentData = [];
|
|
41
|
+
for await (const chunk of body) {
|
|
42
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
43
|
+
const lines = buffer.split("\n");
|
|
44
|
+
buffer = lines.pop() ?? "";
|
|
45
|
+
for (const line of lines) if (line.startsWith("event:")) currentEvent = line.slice(6).trim();
|
|
46
|
+
else if (line.startsWith("data:")) currentData.push(line.slice(5).trim());
|
|
47
|
+
else if (line === "") {
|
|
48
|
+
if (currentData.length > 0) {
|
|
49
|
+
yield {
|
|
50
|
+
event: currentEvent,
|
|
51
|
+
data: currentData.join("\n")
|
|
52
|
+
};
|
|
53
|
+
currentEvent = "message";
|
|
54
|
+
currentData = [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (currentData.length > 0) yield {
|
|
59
|
+
event: currentEvent,
|
|
60
|
+
data: currentData.join("\n")
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/commands/audit.ts
|
|
66
|
+
const API_BASE = "https://api.afterpack.dev";
|
|
67
|
+
const WEB_BASE = "https://afterpack.dev";
|
|
68
|
+
function printUsage() {
|
|
69
|
+
console.log(`
|
|
70
|
+
${bold("Usage:")} afterpack audit ${cyan("<url>")}
|
|
71
|
+
|
|
72
|
+
Example: afterpack audit https://example.com
|
|
73
|
+
`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
function normalizeUrl(input) {
|
|
77
|
+
let url = input.trim();
|
|
78
|
+
if (!/^https?:\/\//i.test(url)) url = `https://${url}`;
|
|
79
|
+
try {
|
|
80
|
+
new URL(url);
|
|
81
|
+
} catch {
|
|
82
|
+
console.error(`\n ${red("Error:")} Invalid URL: ${input}\n`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
return url;
|
|
86
|
+
}
|
|
87
|
+
function clearLine() {
|
|
88
|
+
if (process.stdout.isTTY) process.stdout.write("\x1B[1A\x1B[2K");
|
|
89
|
+
}
|
|
90
|
+
function printPhase(label, done) {
|
|
91
|
+
const icon = done ? green("✓") : yellow("●");
|
|
92
|
+
console.log(` ${icon} ${label}`);
|
|
93
|
+
}
|
|
94
|
+
function printSummaryBox(result) {
|
|
95
|
+
const lines = [];
|
|
96
|
+
if (result.score !== void 0) {
|
|
97
|
+
const scoreColor = result.score >= 70 ? green : result.score >= 40 ? yellow : red;
|
|
98
|
+
lines.push(` Score: ${scoreColor(bold(`${result.score}/100`))}`);
|
|
99
|
+
lines.push("");
|
|
100
|
+
}
|
|
101
|
+
if (result.resources) {
|
|
102
|
+
const r = result.resources;
|
|
103
|
+
const parts = [];
|
|
104
|
+
if (r.unprotected) parts.push(`${r.unprotected} unprotected`);
|
|
105
|
+
if (r.sourceExposed) parts.push(`${r.sourceExposed} source exposed`);
|
|
106
|
+
const detail = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
107
|
+
lines.push(` Resources ${r.total ?? 0} found${detail}`);
|
|
108
|
+
}
|
|
109
|
+
if (result.findings && result.findings.length > 0) {
|
|
110
|
+
const counts = {};
|
|
111
|
+
for (const f of result.findings) {
|
|
112
|
+
const s = f.severity.toLowerCase();
|
|
113
|
+
counts[s] = (counts[s] || 0) + 1;
|
|
114
|
+
}
|
|
115
|
+
const parts = Object.entries(counts).map(([sev, n]) => `${n} ${sev}`);
|
|
116
|
+
lines.push(` Findings ${result.findings.length} issues (${parts.join(", ")})`);
|
|
117
|
+
}
|
|
118
|
+
if (result.techStack && result.techStack.length > 0) lines.push(` Tech Stack ${result.techStack.join(", ")}`);
|
|
119
|
+
if (result.readability !== void 0) lines.push(` Readability ${result.readability}/100`);
|
|
120
|
+
const maxLen = Math.max(...lines.map((l) => stripAnsi(l).length), 50);
|
|
121
|
+
const border = "─".repeat(maxLen + 4);
|
|
122
|
+
console.log(`\n ┌─${border}─┐`);
|
|
123
|
+
for (const line of lines) {
|
|
124
|
+
const pad = maxLen + 4 - stripAnsi(line).length;
|
|
125
|
+
console.log(` │ ${line}${" ".repeat(pad)} │`);
|
|
126
|
+
}
|
|
127
|
+
console.log(` └─${border}─┘`);
|
|
128
|
+
}
|
|
129
|
+
function stripAnsi(str) {
|
|
130
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
131
|
+
}
|
|
132
|
+
async function audit(url) {
|
|
133
|
+
if (!url) {
|
|
134
|
+
printUsage();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const normalizedUrl = normalizeUrl(url);
|
|
138
|
+
console.log(header(version));
|
|
139
|
+
console.log(` Scanning ${cyan(normalizedUrl)} ...\n`);
|
|
140
|
+
const phases = [
|
|
141
|
+
"Fetching page",
|
|
142
|
+
"Rendering JavaScript",
|
|
143
|
+
"Analyzing resources"
|
|
144
|
+
];
|
|
145
|
+
let currentPhaseIndex = -1;
|
|
146
|
+
let extraLinesSincePhase = 0;
|
|
147
|
+
const result = {};
|
|
148
|
+
function advancePhase(phaseName) {
|
|
149
|
+
if (currentPhaseIndex >= 0) {
|
|
150
|
+
if (extraLinesSincePhase === 0) {
|
|
151
|
+
if (process.stdout.isTTY) clearLine();
|
|
152
|
+
printPhase(phases[currentPhaseIndex], true);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (phaseName) {
|
|
156
|
+
let idx = phases.indexOf(phaseName);
|
|
157
|
+
if (idx === -1) {
|
|
158
|
+
phases.push(phaseName);
|
|
159
|
+
idx = phases.length - 1;
|
|
160
|
+
}
|
|
161
|
+
currentPhaseIndex = idx;
|
|
162
|
+
extraLinesSincePhase = 0;
|
|
163
|
+
printPhase(phaseName, false);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const stream = streamSSE(`${API_BASE}/v1/scan`, {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: { "Content-Type": "application/json" },
|
|
170
|
+
body: JSON.stringify({ url: normalizedUrl })
|
|
171
|
+
});
|
|
172
|
+
for await (const event of stream) {
|
|
173
|
+
let payload;
|
|
174
|
+
try {
|
|
175
|
+
payload = JSON.parse(event.data);
|
|
176
|
+
} catch {
|
|
177
|
+
payload = event.data;
|
|
178
|
+
}
|
|
179
|
+
switch (event.event) {
|
|
180
|
+
case "progress":
|
|
181
|
+
advancePhase(payload.phase ?? payload.message);
|
|
182
|
+
break;
|
|
183
|
+
case "finding": {
|
|
184
|
+
const finding = {
|
|
185
|
+
severity: payload.severity ?? "info",
|
|
186
|
+
title: payload.title ?? payload.message ?? "",
|
|
187
|
+
detail: payload.detail
|
|
188
|
+
};
|
|
189
|
+
if (!result.findings) result.findings = [];
|
|
190
|
+
result.findings.push(finding);
|
|
191
|
+
const sev = finding.severity.toLowerCase();
|
|
192
|
+
if (sev === "critical" || sev === "high") {
|
|
193
|
+
const color = severityColor(sev);
|
|
194
|
+
console.log(` ${color("⚠")} ${color(finding.severity)}: ${finding.title}`);
|
|
195
|
+
extraLinesSincePhase++;
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case "score_update":
|
|
200
|
+
result.score = payload.score ?? payload.value;
|
|
201
|
+
break;
|
|
202
|
+
case "complete":
|
|
203
|
+
if (currentPhaseIndex >= 0 && extraLinesSincePhase === 0) {
|
|
204
|
+
if (process.stdout.isTTY) clearLine();
|
|
205
|
+
printPhase(phases[currentPhaseIndex], true);
|
|
206
|
+
}
|
|
207
|
+
if (payload.score !== void 0) result.score = payload.score;
|
|
208
|
+
if (payload.id) result.id = payload.id;
|
|
209
|
+
if (payload.resources) result.resources = payload.resources;
|
|
210
|
+
if (payload.findings) result.findings = payload.findings;
|
|
211
|
+
if (payload.techStack) result.techStack = payload.techStack;
|
|
212
|
+
if (payload.readability !== void 0) result.readability = payload.readability;
|
|
213
|
+
printSummaryBox(result);
|
|
214
|
+
if (result.findings) {
|
|
215
|
+
const critical = result.findings.filter((f) => f.severity.toLowerCase() === "critical");
|
|
216
|
+
if (critical.length > 0) {
|
|
217
|
+
console.log(`\n ${red("⚠")} ${bold(`${critical.length} critical finding${critical.length > 1 ? "s" : ""}:`)}`);
|
|
218
|
+
for (const f of critical) console.log(` ${f.title}${f.detail ? ` (${dim(f.detail)})` : ""}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (result.id) console.log(`\n Full report: ${cyan(`${WEB_BASE}/security-scanner?id=${result.id}`)}\n`);
|
|
222
|
+
else console.log("");
|
|
223
|
+
process.exit(0);
|
|
224
|
+
case "error":
|
|
225
|
+
console.error(`\n ${red("Error:")} ${payload.message ?? payload.error ?? "Scan failed"}\n`);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (result.score !== void 0) printSummaryBox(result);
|
|
230
|
+
console.error(`\n ${yellow("Warning:")} Stream ended without completion event.\n`);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error(`\n ${red("Error:")} ${err.message ?? "Failed to connect to API"}\n`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
4
237
|
|
|
5
238
|
//#endregion
|
|
6
239
|
//#region src/cli.ts
|
|
@@ -9,40 +242,30 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
9
242
|
console.log(`AfterPack v${version}`);
|
|
10
243
|
process.exit(0);
|
|
11
244
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
This will analyze your site and reveal what AI can
|
|
21
|
-
extract from your client-side JavaScript.
|
|
245
|
+
const command = args[0];
|
|
246
|
+
switch (command) {
|
|
247
|
+
case "audit":
|
|
248
|
+
await audit(args[1]);
|
|
249
|
+
break;
|
|
250
|
+
default:
|
|
251
|
+
console.log(`
|
|
252
|
+
AfterPack v${version}
|
|
22
253
|
|
|
23
254
|
┌─────────────────────────────────────────────────────┐
|
|
24
|
-
│
|
|
25
|
-
│ https://www.afterpack.dev
|
|
255
|
+
│ Launching soon — join the waitlist for day-one │
|
|
256
|
+
│ access: https://www.afterpack.dev │
|
|
26
257
|
└─────────────────────────────────────────────────────┘
|
|
27
|
-
`);
|
|
28
|
-
process.exit(0);
|
|
29
|
-
}
|
|
30
|
-
console.log(`
|
|
31
|
-
╔═══════════════════════════════════════════════════════╗
|
|
32
|
-
║ AfterPack v${version.padEnd(42)}║
|
|
33
|
-
╚═══════════════════════════════════════════════════════╝
|
|
34
258
|
|
|
35
|
-
|
|
259
|
+
Usage: afterpack <command> [options]
|
|
36
260
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
automated scraping.
|
|
261
|
+
Commands:
|
|
262
|
+
audit <url> Audit your frontend for leaked secrets & sensitive data
|
|
40
263
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
│ https://www.afterpack.dev │
|
|
44
|
-
└─────────────────────────────────────────────────────┘
|
|
264
|
+
Options:
|
|
265
|
+
-v, --version Show version
|
|
45
266
|
`);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
46
269
|
|
|
47
270
|
//#endregion
|
|
48
271
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../package.json","../src/cli.ts"],"sourcesContent":["{\n \"name\": \"afterpack\",\n \"version\": \"0.0.11\",\n \"description\": \"A high-performance post-build JavaScript obfuscator and minifier\",\n \"type\": \"module\",\n \"main\": \"./dist/cli.js\",\n \"bin\": {\n \"afterpack\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown src/cli.ts --format esm --dts\",\n \"dev\": \"tsdown src/cli.ts --format esm --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"pnpm build\",\n \"prepack\": \"tsx scripts/prepack.ts\",\n \"postpack\": \"tsx scripts/postpack.ts\",\n \"release\": \"npm version patch && git push && git push --tags\",\n \"release:minor\": \"npm version minor && git push && git push --tags\",\n \"release:major\": \"npm version major && git push && git push --tags\"\n },\n \"keywords\": [\n \"obfuscator\",\n \"minifier\",\n \"javascript\",\n \"protection\",\n \"security\",\n \"afterpack\"\n ],\n \"author\": \"AfterPack <hello@afterpack.dev>\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/afterpack-dev/afterpack.git\"\n },\n \"homepage\": \"https://afterpack.dev\",\n \"bugs\": {\n \"url\": \"https://github.com/afterpack-dev/afterpack/issues\"\n },\n \"engines\": {\n \"node\": \">=18\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.0.9\",\n \"tsdown\": \"^0.11.0\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^2.0.0\"\n }\n}\n","#!/usr/bin/env node\n\nimport { version } from \"../package.json\";\n\nconst args = process.argv.slice(2);\n\n// Version flag\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(`AfterPack v${version}`);\n process.exit(0);\n}\n\n// Audit command\nif (args[0] === \"audit\") {\n console.log(`\n ╔═══════════════════════════════════════════════════════╗\n ║ AfterPack v${version.padEnd(42)}║\n ╚═══════════════════════════════════════════════════════╝\n\n Audit command coming soon!\n\n This will analyze your site and reveal what AI can\n extract from your client-side JavaScript.\n\n ┌─────────────────────────────────────────────────────┐\n │ Join the waitlist to get notified: │\n │ https://www.afterpack.dev │\n └─────────────────────────────────────────────────────┘\n`);\n process.exit(0);\n}\n\n// Default: show main waitlist message\nconsole.log(`\n ╔═══════════════════════════════════════════════════════╗\n ║ AfterPack v${version.padEnd(42)}║\n ╚═══════════════════════════════════════════════════════╝\n\n Coming soon: High-performance JavaScript protection.\n\n Secure your client-side logic from AI extraction,\n reverse engineering, vulnerability scanning, and\n automated scraping.\n\n ┌─────────────────────────────────────────────────────┐\n │ Join the waitlist for early access: │\n │ https://www.afterpack.dev │\n └─────────────────────────────────────────────────────┘\n`);\n"],"mappings":";;cAEa;;;;ACEb,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAGlC,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,KAAK,EAAE;AACrD,SAAQ,KAAK,aAAa,QAAQ,EAAE;AACpC,SAAQ,KAAK,EAAE;AAChB;AAGD,IAAI,KAAK,OAAO,SAAS;AACvB,SAAQ,KAAK;;kBAEG,QAAQ,OAAO,GAAG,CAAC;;;;;;;;;;;;EAYnC;AACA,SAAQ,KAAK,EAAE;AAChB;AAGD,QAAQ,KAAK;;kBAEK,QAAQ,OAAO,GAAG,CAAC;;;;;;;;;;;;;EAanC"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":["open: string","close: string","text: string","severity: string","version: string","version","url: string","options?: RequestInit","currentData: string[]","input: string","label: string","done: boolean","result: ScanResult","lines: string[]","parts: string[]","counts: Record<string, number>","str: string","url: string | undefined","phaseName?: string","payload: any","finding: Finding","err: any"],"sources":["../package.json","../src/utils/format.ts","../src/utils/sse.ts","../src/commands/audit.ts","../src/cli.ts"],"sourcesContent":["{\n \"name\": \"afterpack\",\n \"version\": \"0.0.14\",\n \"description\": \"A high-performance post-build JavaScript obfuscator and minifier\",\n \"type\": \"module\",\n \"main\": \"./dist/cli.js\",\n \"bin\": {\n \"afterpack\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown src/cli.ts --format esm --dts\",\n \"dev\": \"tsdown src/cli.ts --format esm --watch\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"pnpm build\",\n \"prepack\": \"tsx scripts/prepack.ts\",\n \"postpack\": \"tsx scripts/postpack.ts\",\n \"release\": \"npm version patch && git push && git push --tags\",\n \"release:minor\": \"npm version minor && git push && git push --tags\",\n \"release:major\": \"npm version major && git push && git push --tags\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\"\n },\n \"keywords\": [\n \"obfuscator\",\n \"minifier\",\n \"javascript\",\n \"protection\",\n \"security\",\n \"afterpack\"\n ],\n \"author\": \"AfterPack <hello@afterpack.dev>\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/afterpack-dev/afterpack.git\"\n },\n \"homepage\": \"https://afterpack.dev\",\n \"bugs\": {\n \"url\": \"https://github.com/afterpack-dev/afterpack/issues\"\n },\n \"engines\": {\n \"node\": \">=18\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.4.4\",\n \"@types/node\": \"^25.0.9\",\n \"tsdown\": \"^0.11.0\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^2.0.0\"\n }\n}\n","const isColorSupported =\n process.env.FORCE_COLOR !== \"0\" &&\n !process.env.NO_COLOR &&\n (process.env.FORCE_COLOR !== undefined || process.stdout.isTTY);\n\nfunction ansi(open: string, close: string) {\n return (text: string) =>\n isColorSupported ? `\\x1b[${open}m${text}\\x1b[${close}m` : text;\n}\n\nexport const bold = ansi(\"1\", \"22\");\nexport const dim = ansi(\"2\", \"22\");\nexport const red = ansi(\"31\", \"39\");\nexport const green = ansi(\"32\", \"39\");\nexport const yellow = ansi(\"33\", \"39\");\nexport const cyan = ansi(\"36\", \"39\");\n\nexport function severityColor(severity: string): (text: string) => string {\n switch (severity.toLowerCase()) {\n case \"critical\":\n return red;\n case \"high\":\n return red;\n case \"medium\":\n return yellow;\n case \"low\":\n return cyan;\n default:\n return dim;\n }\n}\n\nexport function header(version: string): string {\n return `\\n ${bold(`AfterPack v${version}`)} — Security Analyzer\\n`;\n}\n","export interface SSEEvent {\n event: string;\n data: string;\n}\n\nexport async function* streamSSE(\n url: string,\n options?: RequestInit,\n): AsyncGenerator<SSEEvent> {\n const response = await fetch(url, options);\n\n if (!response.ok) {\n throw new Error(\n `SSE request failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const body = response.body;\n if (!body) {\n throw new Error(\"SSE response has no body\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent = \"message\";\n let currentData: string[] = [];\n\n for await (const chunk of body) {\n buffer += decoder.decode(chunk, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event:\")) {\n currentEvent = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n currentData.push(line.slice(5).trim());\n } else if (line === \"\") {\n // Empty line = end of event\n if (currentData.length > 0) {\n yield { event: currentEvent, data: currentData.join(\"\\n\") };\n currentEvent = \"message\";\n currentData = [];\n }\n }\n }\n }\n\n // Flush any remaining event\n if (currentData.length > 0) {\n yield { event: currentEvent, data: currentData.join(\"\\n\") };\n }\n}\n","import { version } from \"../../package.json\";\nimport {\n bold,\n cyan,\n dim,\n green,\n header,\n red,\n severityColor,\n yellow,\n} from \"../utils/format.js\";\nimport { streamSSE } from \"../utils/sse.js\";\n\nconst API_BASE = \"https://api.afterpack.dev\";\nconst WEB_BASE = \"https://afterpack.dev\";\n\ninterface Finding {\n severity: string;\n title: string;\n detail?: string;\n}\n\ninterface ScanResult {\n id?: string;\n score?: number;\n resources?: { total?: number; unprotected?: number; sourceExposed?: number };\n findings?: Finding[];\n techStack?: string[];\n readability?: number;\n}\n\nfunction printUsage(): void {\n console.log(`\n ${bold(\"Usage:\")} afterpack audit ${cyan(\"<url>\")}\n\n Example: afterpack audit https://example.com\n`);\n process.exit(1);\n}\n\nfunction normalizeUrl(input: string): string {\n let url = input.trim();\n if (!/^https?:\\/\\//i.test(url)) {\n url = `https://${url}`;\n }\n // Validate\n try {\n new URL(url);\n } catch {\n console.error(`\\n ${red(\"Error:\")} Invalid URL: ${input}\\n`);\n process.exit(1);\n }\n return url;\n}\n\nfunction clearLine(): void {\n if (process.stdout.isTTY) {\n process.stdout.write(\"\\x1b[1A\\x1b[2K\");\n }\n}\n\nfunction printPhase(label: string, done: boolean): void {\n const icon = done ? green(\"✓\") : yellow(\"●\");\n console.log(` ${icon} ${label}`);\n}\n\nfunction printSummaryBox(result: ScanResult): void {\n const lines: string[] = [];\n\n // Score\n if (result.score !== undefined) {\n const scoreColor =\n result.score >= 70 ? green : result.score >= 40 ? yellow : red;\n lines.push(` Score: ${scoreColor(bold(`${result.score}/100`))}`);\n lines.push(\"\");\n }\n\n // Resources\n if (result.resources) {\n const r = result.resources;\n const parts: string[] = [];\n if (r.unprotected) parts.push(`${r.unprotected} unprotected`);\n if (r.sourceExposed) parts.push(`${r.sourceExposed} source exposed`);\n const detail = parts.length > 0 ? ` (${parts.join(\", \")})` : \"\";\n lines.push(` Resources ${r.total ?? 0} found${detail}`);\n }\n\n // Findings\n if (result.findings && result.findings.length > 0) {\n const counts: Record<string, number> = {};\n for (const f of result.findings) {\n const s = f.severity.toLowerCase();\n counts[s] = (counts[s] || 0) + 1;\n }\n const parts = Object.entries(counts).map(([sev, n]) => `${n} ${sev}`);\n lines.push(\n ` Findings ${result.findings.length} issues (${parts.join(\", \")})`,\n );\n }\n\n // Tech Stack\n if (result.techStack && result.techStack.length > 0) {\n lines.push(` Tech Stack ${result.techStack.join(\", \")}`);\n }\n\n // Readability\n if (result.readability !== undefined) {\n lines.push(` Readability ${result.readability}/100`);\n }\n\n // Box\n const maxLen = Math.max(...lines.map((l) => stripAnsi(l).length), 50);\n const border = \"─\".repeat(maxLen + 4);\n\n console.log(`\\n ┌─${border}─┐`);\n for (const line of lines) {\n const pad = maxLen + 4 - stripAnsi(line).length;\n console.log(` │ ${line}${\" \".repeat(pad)} │`);\n }\n console.log(` └─${border}─┘`);\n}\n\nfunction stripAnsi(str: string): string {\n return str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\nexport async function audit(url: string | undefined): Promise<void> {\n if (!url) {\n printUsage();\n return;\n }\n\n const normalizedUrl = normalizeUrl(url);\n\n console.log(header(version));\n console.log(` Scanning ${cyan(normalizedUrl)} ...\\n`);\n\n const phases = [\n \"Fetching page\",\n \"Rendering JavaScript\",\n \"Analyzing resources\",\n ];\n let currentPhaseIndex = -1;\n let extraLinesSincePhase = 0;\n const result: ScanResult = {};\n\n function advancePhase(phaseName?: string): void {\n // Mark current phase as done\n if (currentPhaseIndex >= 0) {\n if (extraLinesSincePhase === 0) {\n // No findings printed — replace ● with ✓ in place\n if (process.stdout.isTTY) clearLine();\n printPhase(phases[currentPhaseIndex], true);\n }\n // If findings were printed, the ● line stays — no ✓ needed\n }\n\n if (phaseName) {\n // Find phase index or add it\n let idx = phases.indexOf(phaseName);\n if (idx === -1) {\n phases.push(phaseName);\n idx = phases.length - 1;\n }\n currentPhaseIndex = idx;\n extraLinesSincePhase = 0;\n printPhase(phaseName, false);\n }\n }\n\n try {\n const stream = streamSSE(`${API_BASE}/v1/scan`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ url: normalizedUrl }),\n });\n\n for await (const event of stream) {\n let payload: any;\n try {\n payload = JSON.parse(event.data);\n } catch {\n payload = event.data;\n }\n\n switch (event.event) {\n case \"progress\":\n advancePhase(payload.phase ?? payload.message);\n break;\n\n case \"finding\": {\n const finding: Finding = {\n severity: payload.severity ?? \"info\",\n title: payload.title ?? payload.message ?? \"\",\n detail: payload.detail,\n };\n if (!result.findings) result.findings = [];\n result.findings.push(finding);\n\n // Print critical/high findings immediately\n const sev = finding.severity.toLowerCase();\n if (sev === \"critical\" || sev === \"high\") {\n const color = severityColor(sev);\n console.log(\n ` ${color(\"⚠\")} ${color(finding.severity)}: ${finding.title}`,\n );\n extraLinesSincePhase++;\n }\n break;\n }\n\n case \"score_update\":\n result.score = payload.score ?? payload.value;\n break;\n\n case \"complete\":\n // Finalize current phase\n if (currentPhaseIndex >= 0 && extraLinesSincePhase === 0) {\n if (process.stdout.isTTY) clearLine();\n printPhase(phases[currentPhaseIndex], true);\n }\n\n // Merge final data\n if (payload.score !== undefined) result.score = payload.score;\n if (payload.id) result.id = payload.id;\n if (payload.resources) result.resources = payload.resources;\n if (payload.findings) result.findings = payload.findings;\n if (payload.techStack) result.techStack = payload.techStack;\n if (payload.readability !== undefined)\n result.readability = payload.readability;\n\n printSummaryBox(result);\n\n // Critical findings summary\n if (result.findings) {\n const critical = result.findings.filter(\n (f) => f.severity.toLowerCase() === \"critical\",\n );\n if (critical.length > 0) {\n console.log(\n `\\n ${red(\"⚠\")} ${bold(\n `${critical.length} critical finding${\n critical.length > 1 ? \"s\" : \"\"\n }:`,\n )}`,\n );\n for (const f of critical) {\n console.log(\n ` ${f.title}${f.detail ? ` (${dim(f.detail)})` : \"\"}`,\n );\n }\n }\n }\n\n // Report link\n if (result.id) {\n console.log(\n `\\n Full report: ${cyan(\n `${WEB_BASE}/security-scanner?id=${result.id}`,\n )}\\n`,\n );\n } else {\n console.log(\"\");\n }\n\n process.exit(0);\n\n case \"error\":\n console.error(\n `\\n ${red(\"Error:\")} ${\n payload.message ?? payload.error ?? \"Scan failed\"\n }\\n`,\n );\n process.exit(1);\n }\n }\n\n // If stream ends without a complete event, still show what we have\n if (result.score !== undefined) {\n printSummaryBox(result);\n }\n console.error(\n `\\n ${yellow(\"Warning:\")} Stream ended without completion event.\\n`,\n );\n process.exit(1);\n } catch (err: any) {\n console.error(\n `\\n ${red(\"Error:\")} ${err.message ?? \"Failed to connect to API\"}\\n`,\n );\n process.exit(1);\n }\n}\n","#!/usr/bin/env node\n\nimport { version } from \"../package.json\";\nimport { audit } from \"./commands/audit.js\";\n\nconst args = process.argv.slice(2);\n\n// Version flag\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(`AfterPack v${version}`);\n process.exit(0);\n}\n\n// Command routing\nconst command = args[0];\n\nswitch (command) {\n case \"audit\":\n await audit(args[1]);\n break;\n\n default:\n console.log(`\n AfterPack v${version}\n\n ┌─────────────────────────────────────────────────────┐\n │ Launching soon — join the waitlist for day-one │\n │ access: https://www.afterpack.dev │\n └─────────────────────────────────────────────────────┘\n\n Usage: afterpack <command> [options]\n\n Commands:\n audit <url> Audit your frontend for leaked secrets & sensitive data\n\n Options:\n -v, --version Show version\n`);\n break;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "afterpack",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "A high-performance post-build JavaScript obfuscator and minifier",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"node": ">=18"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@biomejs/biome": "^2.4.4",
|
|
36
37
|
"@types/node": "^25.0.9",
|
|
37
38
|
"tsdown": "^0.11.0",
|
|
38
39
|
"tsx": "^4.21.0",
|
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# [AfterPack](https://www.afterpack.dev)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
High-performance JavaScript obfuscator and audit tool — find leaked secrets, seal your production code.
|
|
4
4
|
|
|
5
5
|
[www.afterpack.dev](https://www.afterpack.dev)
|
|
6
6
|
|
|
@@ -10,7 +10,33 @@ A high-performance post-build JavaScript obfuscator and minifier — the final s
|
|
|
10
10
|
npx afterpack
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
| Command | Description |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `npx afterpack audit <url>` | Scan a live URL for leaked secrets and sensitive data |
|
|
18
|
+
| `npx afterpack` | Obfuscate JavaScript build artifacts _(coming soon)_ |
|
|
19
|
+
|
|
20
|
+
## Security Audit
|
|
21
|
+
|
|
22
|
+
Scan any live frontend for leaked secrets, exposed source code, and sensitive data — powered by the [AfterPack API](https://www.afterpack.dev).
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx afterpack audit <url>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Example:**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx afterpack audit example.com
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **Security score** — 0-100 rating based on exposed secrets, source maps, and unprotected resources
|
|
35
|
+
- **Resource analysis** — counts total, unprotected, and source-exposed JavaScript files
|
|
36
|
+
- **Tech stack detection** — identifies frameworks and libraries in use
|
|
37
|
+
- **Web report** — outputs a link to the full interactive report on afterpack.dev
|
|
38
|
+
|
|
39
|
+
## Obfuscation
|
|
14
40
|
|
|
15
41
|
- **Free & Production-Ready** — Full obfuscation at no cost. Pro unlocks multi-file and edge features
|
|
16
42
|
- **20-70x Faster** — Rust-powered engine leaves JavaScript-based tools in the dust
|