Compare commits

..
5 changed files with 60 additions and 21 deletions

No files matched your search

+1 -3
View File
@@ -464,9 +464,7 @@ class ReaJadxBridge {
if (element.getTagName().equals("uses-sdk")) {
minSdk = element.getAttributeNS(android, "minSdkVersion");
targetSdk = element.getAttributeNS(android, "targetSdkVersion");
} else if (element.getTagName().equals("uses-permission")
|| element.getTagName().equals("uses-permission-sdk-23")
|| element.getTagName().equals("uses-permission-sdk-m")) {
} else if (element.getTagName().equals("uses-permission")) {
String permission = element.getAttributeNS(android, "name");
if (!permission.isEmpty()) permissions.add(permission);
}
@@ -99,6 +99,41 @@ describe.skipIf(process.platform === "win32")(
},
);
describe.skipIf(process.platform === "win32")(
"desktop entry Unicode values",
() => {
it.each(["\u2028", "\u2029"])(
"retains Unicode separator %j in desktop launch arguments",
async (separator) => {
const { home, bin, environment } = await fixture();
const desktopEntry = join(home, "unicode.desktop");
await writeFile(
desktopEntry,
`[Desktop Entry]\nType=Application\nName=Notes${separator}Editor\nExec=notes-app --title "A${separator}B" %F\n`,
);
const inspected = await inspectLinuxApplication({
desktopEntry,
executable: undefined,
arguments: ["today.md"],
environment,
home,
callerDirectory: home,
});
expect(inspected).toMatchObject({
ok: true,
value: {
inventory: {
argv: ["notes-app", "--title", `A${separator}B`, "today.md"],
desktop_entry: { name: `Notes${separator}Editor` },
executable: { path: join(bin, "notes-app") },
},
},
});
},
);
},
);
describe.skipIf(process.platform === "win32")("inspectLinuxApplication", () => {
it("resolves a desktop entry id into a launch command and state directories", async () => {
const { home, bin, environment } = await fixture();
+17
View File
@@ -60,6 +60,23 @@ describe("parseDesktopEntry", () => {
error: "missing_desktop_entry_group",
});
});
it.each(["\u2028", "\u2029"])(
"keeps Unicode separator %j inside string and list values",
(separator) => {
const parsed = parseDesktopEntry(
`[Desktop Entry]\nType=Application\nName=${separator}Notes${separator}\nExec=notes --title "A${separator}B"\nCategories=Utility;A${separator}B;\n`,
);
expect(parsed).toMatchObject({
ok: true,
value: {
name: `${separator}Notes${separator}`,
exec: `notes --title "A${separator}B"`,
categories: ["Utility", `A${separator}B`],
},
});
},
);
});
describe("tokenizeDesktopExec", () => {
+2 -1
View File
@@ -15,7 +15,8 @@ export interface DesktopEntry {
}
const GROUP = /^\[(?<name>[^\]]+)\]$/u;
const ENTRY = /^(?<key>[A-Za-z0-9-]+)\s*=\s*(?<value>.*)$/u;
// Only LF separates records; Unicode line/paragraph separators are value data.
const ENTRY = /^(?<key>[A-Za-z0-9-]+)[ \t]*=[ \t]*(?<value>.*)$/su;
const VALUE_ESCAPES: Readonly<Record<string, string>> = {
s: " ",
n: "\n",
@@ -51,14 +51,8 @@ public class ReaJadxMetadataRegression {
+ prefix + ":versionName=\"" + encoded + "\" " + prefix + ":versionCode=\"7\""
+ " other:versionName=\"decoy\"><uses-sdk " + prefix + ":minSdkVersion=\"23\" "
+ prefix + ":targetSdkVersion=\"35\"/><uses-permission " + prefix
+ ":name=\"org.example.MARKER\"/><uses-permission-sdk-23 " + prefix
+ ":name=\"org.example.SDK23\"/><uses-permission-sdk-m " + prefix
+ ":name=\"org.example.SDKM\"/><other:uses-permission-sdk-23 " + prefix
+ ":name=\"foreign.element\"/><uses-permission-sdk-23 other:name=\"foreign.attribute\"/>"
+ "<uses-permission-sdk-230 " + prefix + ":name=\"unknown.tag\"/>"
+ "<application><uses-permission " + prefix + ":name=\"nested.decoy\"/>"
+ "<uses-permission-sdk-23 " + prefix + ":name=\"nested.sdk23\"/>"
+ "<uses-permission-sdk-m " + prefix + ":name=\"nested.sdkm\"/></application></manifest>";
+ ":name=\"org.example.MARKER\"/><application><uses-permission " + prefix
+ ":name=\"nested.decoy\"/></application></manifest>";
Map<String, Object> summary = ReaJadxBridge.manifestSummary(xml);
require(summary.get("package").equals("org.example.root"), "Wrong manifest package");
require(summary.get("version_name").equals(
@@ -66,9 +60,8 @@ public class ReaJadxMetadataRegression {
"Version name lost XML entity identity: " + summary);
require(summary.get("version_code").equals("7") && summary.get("min_sdk").equals("23")
&& summary.get("target_sdk").equals("35"), "Namespace prefix changed summary values");
require(summary.get("permissions").equals(
List.of("org.example.MARKER", "org.example.SDK23", "org.example.SDKM")),
"Permission summary lost SDK-gated declarations or included unrelated declarations");
require(summary.get("permissions").equals(List.of("org.example.MARKER")),
"Permission summary included a nested or unrelated declaration");
}
}
for (String xml : List.of("<other/>", "<manifest><application></manifest>",
@@ -87,9 +80,7 @@ public class ReaJadxMetadataRegression {
for (boolean assetFirst : List.of(true, false)) {
String pkg = assetFirst ? "org.example.first" : "org.example.second";
String root = "<manifest xmlns:a=\"http://schemas.android.com/apk/res/android\" package=\""
+ pkg + "\" a:versionName=\"one &amp; beta\" a:versionCode=\"7\">"
+ "<uses-permission-sdk-23 a:name=\"org.example.SDK23\"/>"
+ "<uses-permission-sdk-m a:name=\"org.example.SDKM\"/></manifest>";
+ pkg + "\" a:versionName=\"one &amp; beta\" a:versionCode=\"7\"/>";
String asset = "<manifest package=\"org.example.asset\"/>";
Path apk = output.resolve(assetFirst ? "manifest-asset-first.apk" : "manifest-root-first.apk");
try (ZipOutputStream zip = new ZipOutputStream(Files.newOutputStream(apk))) {
@@ -108,9 +99,6 @@ public class ReaJadxMetadataRegression {
JsonObject summary = call(handle, bridge, "get_app_info", new JsonObject());
require(summary.get("package").getAsString().equals(pkg), "Asset or previous session supplied package facts");
require(summary.get("version_name").getAsString().equals("one & beta"), "Wrong root version value");
require(summary.getAsJsonArray("permissions").equals(JsonParser.parseString(
"[\"org.example.SDK23\",\"org.example.SDKM\"]")),
"Root manifest SDK-gated permissions were omitted");
String raw = holder.current().getResources().stream()
.filter(resource -> resource.getOriginalName().equals("AndroidManifest.xml"))
.findFirst().orElseThrow().loadContent().getText().getCodeStr();