Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d4dce1263 | ||
|
|
1e034a80fe | ||
|
|
e28762a5c7 | ||
|
|
c1f1570a0c | ||
|
|
bcc3ad6014 | ||
|
|
258ecd361b |
@@ -157,7 +157,7 @@ const runApktool = async (
|
|||||||
timeoutMs: limits.timeoutMs,
|
timeoutMs: limits.timeoutMs,
|
||||||
diagnosticBytes: limits.maxOutputBytes,
|
diagnosticBytes: limits.maxOutputBytes,
|
||||||
},
|
},
|
||||||
(signal === undefined ? {} : { signal }),
|
signal === undefined ? {} : { signal },
|
||||||
);
|
);
|
||||||
return ok({ stdout: output.stdout.text, stderr: output.stderr.text });
|
return ok({ stdout: output.stdout.text, stderr: output.stderr.text });
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
|
|||||||
@@ -130,7 +130,9 @@ export const planIntegrationMaintenance = async (
|
|||||||
};
|
};
|
||||||
if (plan.data.plannedActions.length === 0)
|
if (plan.data.plannedActions.length === 0)
|
||||||
return { status: "current", plannedActions: [] };
|
return { status: "current", plannedActions: [] };
|
||||||
const allowedIds = new Set(scope.clients.map((client) => `configure_client:${client}`));
|
const allowedIds = new Set(
|
||||||
|
scope.clients.map((client) => `configure_client:${client}`),
|
||||||
|
);
|
||||||
const skillTargets = new Set(skillDestinations.map(({ path }) => path));
|
const skillTargets = new Set(skillDestinations.map(({ path }) => path));
|
||||||
if (
|
if (
|
||||||
plan.data.plannedActions.some((action) =>
|
plan.data.plannedActions.some((action) =>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { parse } from "@babel/parser";
|
||||||
|
import { expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { collectJavaScriptExports } from "./javascriptAstFingerprint.js";
|
||||||
|
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
"exports[key] = 1;",
|
||||||
|
"module[key].value = 1;",
|
||||||
|
'module["exports.value"] = 1;',
|
||||||
|
"module.exports = { [key]: 1 };",
|
||||||
|
'Object[method](exports, "value", {});',
|
||||||
|
"function d() {} d(exports, { invented: 1 });",
|
||||||
|
])("does not invent an export name for %s", (source) => {
|
||||||
|
expect(collectJavaScriptExports(parse(source)).values).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
'exports[""] = 1;',
|
||||||
|
'module.exports = { "": 1 };',
|
||||||
|
'Object.defineProperty(exports, "", {});',
|
||||||
|
])("preserves the exact empty export name for %s", (source) => {
|
||||||
|
expect(collectJavaScriptExports(parse(source)).values).toEqual([""]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("retains static computed and ordinary names", () => {
|
||||||
|
expect(
|
||||||
|
collectJavaScriptExports(
|
||||||
|
parse('module["exports"]["ready"] = 1; exports.done = 2;'),
|
||||||
|
).values,
|
||||||
|
).toEqual(["done", "ready"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("retains member helper exports on this", () => {
|
||||||
|
expect(
|
||||||
|
collectJavaScriptExports(parse("this.d(exports, { ready: () => 1 });"))
|
||||||
|
.values,
|
||||||
|
).toEqual(["ready"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not expose invented names in bundler module analysis", () => {
|
||||||
|
const analysis = analyzeJavaScriptStaticSource(`
|
||||||
|
globalThis.webpackChunkApp.push([[1], { 1: function(module, exports) {
|
||||||
|
exports[key] = 1;
|
||||||
|
module["exports.value"] = 2;
|
||||||
|
module.exports = { [key]: 3, ready: 4 };
|
||||||
|
} }]);
|
||||||
|
`);
|
||||||
|
expect(analysis.parse_status).toBe("complete");
|
||||||
|
expect(analysis.bundler_registrations[0]?.modules[0]?.exports).toEqual([
|
||||||
|
"ready",
|
||||||
|
]);
|
||||||
|
});
|
||||||
@@ -5,7 +5,10 @@ import * as t from "@babel/types";
|
|||||||
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
||||||
|
|
||||||
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
import { compareUnicodeCodePoints } from "../unicodeCodePointOrder.js";
|
||||||
import { propertyName } from "./javascriptAstValues.js";
|
import {
|
||||||
|
propertyName,
|
||||||
|
semanticStaticPropertyKey,
|
||||||
|
} from "./javascriptAstValues.js";
|
||||||
import { stringValue } from "./javascriptStaticAnalysisHelpers.js";
|
import { stringValue } from "./javascriptStaticAnalysisHelpers.js";
|
||||||
|
|
||||||
/** Static CommonJS export names. */
|
/** Static CommonJS export names. */
|
||||||
@@ -71,15 +74,20 @@ const collectAssignmentExports = (
|
|||||||
output: Set<string>,
|
output: Set<string>,
|
||||||
): void => {
|
): void => {
|
||||||
const path = memberPath(left);
|
const path = memberPath(left);
|
||||||
if (path === "exports" || path === "module.exports") {
|
if (path === null) return;
|
||||||
|
const exportOffset =
|
||||||
|
path[0] === "exports"
|
||||||
|
? 1
|
||||||
|
: path[0] === "module" && path[1] === "exports"
|
||||||
|
? 2
|
||||||
|
: null;
|
||||||
|
if (exportOffset === null) return;
|
||||||
|
if (path.length === exportOffset) {
|
||||||
if (t.isObjectExpression(right)) collectObjectKeys(right, output);
|
if (t.isObjectExpression(right)) collectObjectKeys(right, output);
|
||||||
else addExport(output, "default");
|
else addExport(output, "default");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (path.startsWith("exports."))
|
addExport(output, path.slice(exportOffset).join("."));
|
||||||
addExport(output, path.slice("exports.".length));
|
|
||||||
if (path.startsWith("module.exports."))
|
|
||||||
addExport(output, path.slice("module.exports.".length));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const collectCallExports = (
|
const collectCallExports = (
|
||||||
@@ -87,19 +95,28 @@ const collectCallExports = (
|
|||||||
output: Set<string>,
|
output: Set<string>,
|
||||||
): void => {
|
): void => {
|
||||||
const callee = memberPath(call.callee);
|
const callee = memberPath(call.callee);
|
||||||
if (callee === "Object.defineProperty") {
|
if (callee === null) return;
|
||||||
|
const definePropertyCall =
|
||||||
|
callee.length === 2 &&
|
||||||
|
callee[0] === "Object" &&
|
||||||
|
callee[1] === "defineProperty";
|
||||||
|
const declarationCall = callee.length >= 2 && callee.at(-1) === "d";
|
||||||
|
if (!definePropertyCall && !declarationCall) return;
|
||||||
const target = memberPath(call.arguments[0]);
|
const target = memberPath(call.arguments[0]);
|
||||||
|
const exportTarget =
|
||||||
|
target !== null &&
|
||||||
|
((target.length === 1 && target[0] === "exports") ||
|
||||||
|
(target.length === 2 &&
|
||||||
|
target[0] === "module" &&
|
||||||
|
target[1] === "exports"));
|
||||||
|
if (!exportTarget) return;
|
||||||
|
if (definePropertyCall) {
|
||||||
const name = stringValue(call.arguments[1]);
|
const name = stringValue(call.arguments[1]);
|
||||||
if ((target === "exports" || target === "module.exports") && name)
|
if (name !== undefined) addExport(output, name);
|
||||||
addExport(output, name);
|
return;
|
||||||
}
|
}
|
||||||
if (!callee.endsWith(".d")) return;
|
|
||||||
const target = memberPath(call.arguments[0]);
|
|
||||||
const declarations = call.arguments[1];
|
const declarations = call.arguments[1];
|
||||||
if (
|
if (t.isObjectExpression(declarations))
|
||||||
(target === "exports" || target === "module.exports") &&
|
|
||||||
t.isObjectExpression(declarations)
|
|
||||||
)
|
|
||||||
collectObjectKeys(declarations, output);
|
collectObjectKeys(declarations, output);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,8 +126,8 @@ const collectObjectKeys = (
|
|||||||
): void => {
|
): void => {
|
||||||
for (const property of object.properties) {
|
for (const property of object.properties) {
|
||||||
if (!t.isObjectProperty(property) && !t.isObjectMethod(property)) continue;
|
if (!t.isObjectProperty(property) && !t.isObjectMethod(property)) continue;
|
||||||
const name = propertyName(property.key);
|
const name = semanticStaticPropertyKey(property.key, property.computed);
|
||||||
if (name !== "") addExport(output, name);
|
if (name !== null) addExport(output, name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,7 +135,9 @@ const addExport = (output: Set<string>, name: string): void => {
|
|||||||
output.add(name);
|
output.add(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const memberPath = (node: t.Node | null | undefined): string => {
|
const memberPath = (
|
||||||
|
node: t.Node | null | undefined,
|
||||||
|
): readonly string[] | null => {
|
||||||
const properties: string[] = [];
|
const properties: string[] = [];
|
||||||
let current = node;
|
let current = node;
|
||||||
while (
|
while (
|
||||||
@@ -126,23 +145,19 @@ const memberPath = (node: t.Node | null | undefined): string => {
|
|||||||
current !== null &&
|
current !== null &&
|
||||||
(t.isMemberExpression(current) || t.isOptionalMemberExpression(current))
|
(t.isMemberExpression(current) || t.isOptionalMemberExpression(current))
|
||||||
) {
|
) {
|
||||||
properties.push(propertyName(current.property));
|
const property = semanticStaticPropertyKey(
|
||||||
current = t.isNode(current.object) ? current.object : undefined;
|
current.property,
|
||||||
|
current.computed,
|
||||||
|
);
|
||||||
|
if (property === null) return null;
|
||||||
|
properties.push(property);
|
||||||
|
current = current.object;
|
||||||
}
|
}
|
||||||
|
if (current === undefined || current === null) return null;
|
||||||
const root =
|
const root = t.isIdentifier(current)
|
||||||
current !== undefined && t.isIdentifier(current)
|
|
||||||
? current.name
|
? current.name
|
||||||
: current !== undefined && t.isThisExpression(current)
|
: t.isThisExpression(current)
|
||||||
? "this"
|
? "this"
|
||||||
: "";
|
: null;
|
||||||
if (properties.length === 0) return root;
|
return root === null ? null : [root, ...properties.reverse()];
|
||||||
|
|
||||||
properties.reverse();
|
|
||||||
const path = root === "" ? [] : [root];
|
|
||||||
for (const property of properties) {
|
|
||||||
if (path.length === 0 && property === "") continue;
|
|
||||||
path.push(property);
|
|
||||||
}
|
|
||||||
return path.join(".");
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ it("collects a 12,000-member CommonJS export path without recursion", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("preserves empty, dynamic, this, and private member path behavior", () => {
|
it("preserves empty keys without inventing dynamic, this, or private exports", () => {
|
||||||
const file = parseJavaScriptSource(`
|
const file = parseJavaScriptSource(`
|
||||||
exports[""].value = 1;
|
exports[""].value = 1;
|
||||||
exports[""] = 0;
|
exports[""] = 0;
|
||||||
@@ -24,11 +24,7 @@ it("preserves empty, dynamic, this, and private member path behavior", () => {
|
|||||||
class Example { #private = 0; read() { this.#private = 1; } }
|
class Example { #private = 0; read() { this.#private = 1; } }
|
||||||
`);
|
`);
|
||||||
if (file === null) throw new Error("Expected valid JavaScript");
|
if (file === null) throw new Error("Expected valid JavaScript");
|
||||||
expect(collectJavaScriptExports(file).values).toEqual([
|
expect(collectJavaScriptExports(file).values).toEqual(["", ".value"]);
|
||||||
"",
|
|
||||||
".value",
|
|
||||||
"key.value",
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps left-first and outer-member-first bundler runtime matches", () => {
|
it("keeps left-first and outer-member-first bundler runtime matches", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user