Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Zinnia! A cross platform 7Z gui frontend built on Tauri V2!

- **Security - Downloaded files:** On macOS, files extracted from a downloaded archive now keep the download warning (quarantine), so Gatekeeper still checks apps that came out of an archive. Converting or updating a downloaded archive now keeps its download warning on macOS and Windows. The new archive is marked before it is saved, so an unmarked copy is never left on disk.
- **Security - Windows:** Extracted folders no longer keep the read-only or system attribute. An archive can no longer make Explorer apply its own `desktop.ini` (for example, a folder icon loaded from a network share) to the folders it creates.
- **Security - Encryption:** Adding files with a password to an existing ZIP archive that does not end in `.zip` now always uses AES-256 instead of the weaker ZipCrypto.
- **Security - Encryption:** As an extra safeguard, the backend now always uses AES-256 instead of the weaker ZipCrypto when files are added with a password to an existing ZIP archive, even one whose name does not end in `.zip`.
- **Security - Paths:** Folder names that Zinnia derives from an archive name now avoid Windows device names (such as `CON` or `NUL`), `:` streams, and shell folder IDs (`name.{GUID}`) on every platform. On Windows, **Open destination** refuses folders with such names.
- **Security - Updater:** Zinnia never installs an update whose version is not newer than the installed one. Update signatures with a non-standard encoding are now rejected.
- **Security:** Progress updates (including file names) and debug console output now only reach the window that started the operation, instead of every open window.
Expand Down
48 changes: 32 additions & 16 deletions e2e/specs/release-safeguards.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { requireHostSidecar, run7z } from "../../scripts/archive-fixtures.js";
import {
applyIncomingPaths,
queueDialogResult,
setInputValue,
startPowerExtract,
switchToPowerWorkspace,
waitForArchiveIdle,
Expand Down Expand Up @@ -254,7 +253,7 @@ describe("beta.5 release safeguards", () => {
});
});

it("encrypts files added with a password to a ZIP without a .zip name using AES-256", async () => {
it("encrypts files added over IPC with a password to a ZIP without a .zip name using AES-256", async () => {
const hello = path.join(ROOT, "legacy-hello.txt");
fs.writeFileSync(hello, PAYLOAD);
const archive = path.join(ROOT, "legacy.bin");
Expand All @@ -267,21 +266,38 @@ describe("beta.5 release safeguards", () => {
fs.writeFileSync(added, "added with a password\n");
const password = "Zinnia-E2E-Secret-5";

await openInBrowse(archive, "legacy-hello.txt");
await setInputValue("#browse-password", password);
await queueDialogResult([added]);
await browser.execute(() => {
document.getElementById("browse-add-files")?.click();
});
await browser.waitUntil(
() => listArchive(archive).includes("secret-added.txt"),
{
timeout: 120_000,
interval: 250,
timeoutMsg: "update did not add secret-added.txt",
},
);
await waitForArchiveIdle();
const token = await browser.execute(
(target) =>
window.__TAURI_INTERNALS__.invoke("archive_output_selection_token", {
path: target,
}),
archive,
);
assert.ok(token && token !== "absent", "no output token for the archive");
const result = await browser.execute(
(requestJson) =>
window.__TAURI_INTERNALS__.invoke("run_7z", { requestJson }),
JSON.stringify({
args: [
"u",
"-sse",
"-snl",
"-snh",
"-spd",
`-p${password}`,
archive,
"--",
added,
],
expectedArchiveIdentity: token,
}),
);
assert.equal(result.code, 0, `run_7z update failed: ${result.stderr}`);
assert.ok(
listArchive(archive).includes("secret-added.txt"),
"update did not add secret-added.txt",
);

const methods = memberMethods(listArchive(archive, password));
const method = methods.get("secret-added.txt") ?? "";
Expand Down
3 changes: 2 additions & 1 deletion scripts/github-cli.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ function releaseAssetUploadArgs(uploadUrl, filePath) {
];
}

function runGitHub(args, { input } = {}) {
function runGitHub(args, { input, cwd } = {}) {
const result = spawnSync("gh", args, {
encoding: "utf8",
env: githubCliEnvironment(),
input,
cwd,
stdio: ["pipe", "pipe", "pipe"],
maxBuffer: 16 * 1024 * 1024,
});
Expand Down
13 changes: 6 additions & 7 deletions scripts/verify-release-draft.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const {
githubApi,
githubApiRaw,
assertGitHubCliAuthenticated,
githubCliEnvironment,
runGitHub,
} = require("./github-cli.cjs");
const { isExplicitTruthy } = require("./release-policy.cjs");
const {
Expand Down Expand Up @@ -376,12 +376,11 @@ function currentHeadCommit() {
}

function githubAuthToken() {
return execSync("gh auth token --hostname github.com", {
cwd: root,
encoding: "utf8",
env: githubCliEnvironment(),
stdio: ["ignore", "pipe", "pipe"],
}).trim();
return String(
runGitHub(["auth", "token", "--hostname", "github.com"], {
cwd: root,
}).stdout || "",
).trim();
}

async function downloadDraftAsset(
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading