Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4116ecd4a | ||
|
|
29ba54d5b6 | ||
|
|
e3dd0e44da |
@@ -1444,8 +1444,8 @@ view would leave a wallet one click from deletion.
|
|||||||
without it, the lost-password route on DeleteWallet is the first they
|
without it, the lost-password route on DeleteWallet is the first they
|
||||||
would hear of it. The hint line reserves its height, so switching tabs
|
would hear of it. The hint line reserves its height, so switching tabs
|
||||||
cannot move the password fields under the pointer.
|
cannot move the password fields under the pointer.
|
||||||
- Error line
|
- "Import" button, with the error line beside it so that it adds no height
|
||||||
- "Import" button
|
to a screen whose button already starts near the bottom of the popup
|
||||||
- **Transitions**:
|
- **Transitions**:
|
||||||
- "Import" with a valid entry and a matching password of at least 12
|
- "Import" with a valid entry and a matching password of at least 12
|
||||||
characters → creates the wallet, clears the navigation stack, and →
|
characters → creates the wallet, clears the navigation stack, and →
|
||||||
|
|||||||
@@ -23,25 +23,24 @@
|
|||||||
|
|
||||||
pre-1.0, working towards the 1.0.0 milestone. Tagged v0.1.0 on 2026-02-27. The
|
pre-1.0, working towards the 1.0.0 milestone. Tagged v0.1.0 on 2026-02-27. The
|
||||||
milestone is in flight on `next`; its `next` -> `main` PR is
|
milestone is in flight on `next`; its `next` -> `main` PR is
|
||||||
[#190](https://git.eeqj.de/sneak/AutistMask/pulls/190). `make check` verified
|
[#388](https://git.eeqj.de/sneak/AutistMask/pulls/388). `make build` produces
|
||||||
green on `next` at `e9fa8be` on 2026-08-10, and `make build` produces
|
`dist/chrome/` and `dist/firefox/` with `DEBUG` compiled off, and checks them
|
||||||
`dist/chrome/` and `dist/firefox/`, verified against the build's own receipt to
|
against the build's own receipt to hold exactly the regular files and symlinks
|
||||||
hold exactly the regular files and symlinks that build emitted, with `DEBUG`
|
that build emitted.
|
||||||
compiled off.
|
|
||||||
|
|
||||||
The backlog lives on the
|
The backlog lives on the
|
||||||
[Gitea tracker](https://git.eeqj.de/sneak/AutistMask/issues), which is
|
[Gitea tracker](https://git.eeqj.de/sneak/AutistMask/issues), which is
|
||||||
authoritative; this file does not duplicate it. Full policy file set present.
|
authoritative; this file does not duplicate it. Full policy file set present.
|
||||||
Real-browser end-to-end suites (`make test-e2e` for Chrome,
|
Real-browser end-to-end suites (`make test-e2e` for Chrome,
|
||||||
`make test-e2e-firefox` for Firefox) sit alongside `make check`, which now does
|
`make test-e2e-firefox` for Firefox) sit alongside `make check`, which runs the
|
||||||
static analysis as well as formatting, and `.gitea/workflows/e2e.yml` runs both
|
tests, static analysis and the formatting check, and `.gitea/workflows/e2e.yml`
|
||||||
of them on every push.
|
runs both of them on every push.
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Pre-1.0 security review of the extension (key handling, DEBUG mode policy, RPC
|
Cut 1.0.0 once the
|
||||||
input validation) before any 1.0rc tag. Individual filed issues are parts of it,
|
[1.0.0 milestone](https://git.eeqj.de/sneak/AutistMask/milestone/6) is empty,
|
||||||
but the review is broader than any of them.
|
then continue tagging as milestones land.
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
@@ -51,11 +50,48 @@ but the review is broader than any of them.
|
|||||||
the send confirmation and approval screens already did. The add wallet screen
|
the send confirmation and approval screens already did. The add wallet screen
|
||||||
showed a missing, short or mismatched password in the flash line, and the
|
showed a missing, short or mismatched password in the flash line, and the
|
||||||
private key export, recovery phrase and delete wallet screens each had a line
|
private key export, recovery phrase and delete wallet screens each had a line
|
||||||
of their own above the field. Each line clears when the screen is shown again
|
of their own above the field. On the add wallet screen the line sits beside
|
||||||
and when the password is tried again. The add wallet screen's other messages,
|
the Import button, so the button stays where it was at 360x600. Each line
|
||||||
such as an invalid recovery phrase, a duplicate wallet and the address scan,
|
clears when the screen is shown again and when the password is tried again.
|
||||||
stay in the flash line. `tests/passwordErrorLines.test.js` drives all four
|
The add wallet screen's other messages, such as an invalid recovery phrase, a
|
||||||
screens in the popup.
|
duplicate wallet and the address scan, stay in the flash line.
|
||||||
|
`tests/passwordErrorLines.test.js` drives all four screens in the popup.
|
||||||
|
|
||||||
|
- 2026-10-07: Pre-1.0 security review of the extension
|
||||||
|
([#383](https://git.eeqj.de/sneak/AutistMask/issues/383)), reading the tree at
|
||||||
|
`99292b9` for key handling, the DEBUG mode policy, and what the background
|
||||||
|
accepts from pages, the configured RPC endpoint and the explorer, with what
|
||||||
|
the approval screens show from it; the site permission model and storage were
|
||||||
|
read as well. Its summary on that issue lists ten findings, each filed as its
|
||||||
|
own issue, and all ten are fixed on `next`; one,
|
||||||
|
[#399](https://git.eeqj.de/sneak/AutistMask/issues/399), put funds at risk.
|
||||||
|
Three decisions it raised are still open with the owner: the Argon2id cost for
|
||||||
|
the vault key ([#401](https://git.eeqj.de/sneak/AutistMask/issues/401)), a
|
||||||
|
connected site switching the network with no prompt
|
||||||
|
([#408](https://git.eeqj.de/sneak/AutistMask/issues/408)), and `eth_sign`
|
||||||
|
signing as a personal message
|
||||||
|
([#409](https://git.eeqj.de/sneak/AutistMask/issues/409)). Not covered: the
|
||||||
|
end-to-end suites were not run, the bundled phishing blocklist and token list
|
||||||
|
were not checked entry by entry, `ethers` and `libsodium-wrappers-sumo` were
|
||||||
|
taken as audited, and nothing was tried against a real network with real funds
|
||||||
|
([#385](https://git.eeqj.de/sneak/AutistMask/issues/385)). The planned
|
||||||
|
independent second check of each finding did not run; the findings rest on the
|
||||||
|
reviewer's own reading of the code.
|
||||||
|
|
||||||
|
- 2026-10-07: Stale branches pruned from `origin`
|
||||||
|
([#167](https://git.eeqj.de/sneak/AutistMask/issues/167)). The issue
|
||||||
|
classifies each branch it lists, with the evidence. The eighteen still on
|
||||||
|
`origin` that it classifies as landed, or superseded by merged pull requests,
|
||||||
|
were deleted. `feat/message-signing` and `fix/59-transaction-view-ui-policies`
|
||||||
|
had been deleted on 2026-09-09; both landed and stay deleted. Four are kept
|
||||||
|
because their work is not in `next`: `fix/consistent-error-display`,
|
||||||
|
`fix/87-consistent-error-display` and `fix/87-consistent-error-display-v2`,
|
||||||
|
the change for [#87](https://git.eeqj.de/sneak/AutistMask/issues/87) that
|
||||||
|
never landed, as reference for
|
||||||
|
[#493](https://git.eeqj.de/sneak/AutistMask/issues/493); and
|
||||||
|
`chore/token-list-enrichment`, deleted on 2026-09-09 and re-created at
|
||||||
|
`f7a2437`, whose `scripts/` tooling waits on
|
||||||
|
[#495](https://git.eeqj.de/sneak/AutistMask/issues/495).
|
||||||
|
|
||||||
- 2026-10-07: The README says that the wallet never clears the clipboard after
|
- 2026-10-07: The README says that the wallet never clears the clipboard after
|
||||||
the private key or the recovery phrase is copied, and why
|
the private key or the recovery phrase is copied, and why
|
||||||
@@ -1884,6 +1920,3 @@ but the review is broader than any of them.
|
|||||||
|
|
||||||
Only work that has no issue of its own belongs here; everything else is on the
|
Only work that has no issue of its own belongs here; everything else is on the
|
||||||
tracker.
|
tracker.
|
||||||
|
|
||||||
- Cut 1.0.0 once the milestone is empty, then continue tagging as milestones
|
|
||||||
land.
|
|
||||||
|
|||||||
+16
-10
@@ -207,16 +207,22 @@
|
|||||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<!-- The error line sits beside Import, not above it: at
|
||||||
id="add-wallet-password-error"
|
360x600 the button already starts near the bottom of
|
||||||
class="text-xs mb-2 min-h-[1.25rem] invisible"
|
the popup, and a line of its own would push it below
|
||||||
></div>
|
the fold. The longest error fits on one line here. -->
|
||||||
<button
|
<div class="flex items-center gap-2">
|
||||||
id="btn-add-wallet-confirm"
|
<button
|
||||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
id="btn-add-wallet-confirm"
|
||||||
>
|
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||||
Import
|
>
|
||||||
</button>
|
Import
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
id="add-wallet-password-error"
|
||||||
|
class="text-xs min-h-[1.25rem] invisible"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ============ MAIN VIEW: ALL WALLETS & ADDRESSES ============ -->
|
<!-- ============ MAIN VIEW: ALL WALLETS & ADDRESSES ============ -->
|
||||||
|
|||||||
+103
-2
@@ -1994,11 +1994,11 @@ test("an over-long flash message keeps to one line (#252)", async (env) => {
|
|||||||
|
|
||||||
// Every screen that asks for a password reserves room for one line of error.
|
// Every screen that asks for a password reserves room for one line of error.
|
||||||
// The two on the dApp approval screens also have a border and padding, which
|
// The two on the dApp approval screens also have a border and padding, which
|
||||||
// that reserved height has to cover too.
|
// that reserved height has to cover too. The add wallet screen's line sits
|
||||||
|
// beside its button rather than above it, and has its own test below.
|
||||||
const PASSWORD_ERROR_CONTAINERS = [
|
const PASSWORD_ERROR_CONTAINERS = [
|
||||||
"approve-tx-error",
|
"approve-tx-error",
|
||||||
"approve-sign-error",
|
"approve-sign-error",
|
||||||
"add-wallet-password-error",
|
|
||||||
"export-privkey-password-error",
|
"export-privkey-password-error",
|
||||||
"show-phrase-password-error",
|
"show-phrase-password-error",
|
||||||
"delete-wallet-password-error",
|
"delete-wallet-password-error",
|
||||||
@@ -2066,6 +2066,107 @@ test("a password error moves nothing on any screen (#297)", async (env) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ------------------------------------------ add wallet Import button (#493)
|
||||||
|
|
||||||
|
// At 360x600 the add wallet screen's Import button already starts near the
|
||||||
|
// bottom of the popup, so its password error line sits beside the button
|
||||||
|
// rather than above it. Measures the button and the line empty and again
|
||||||
|
// filled with the longest error addWallet.js puts there. Runs in the page.
|
||||||
|
function measureImportButton() {
|
||||||
|
const button = document.getElementById("btn-add-wallet-confirm");
|
||||||
|
const line = document.getElementById("add-wallet-password-error");
|
||||||
|
const measure = () => {
|
||||||
|
const b = button.getBoundingClientRect();
|
||||||
|
const l = line.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
buttonTop: b.top + window.scrollY,
|
||||||
|
buttonBottom: b.bottom + window.scrollY,
|
||||||
|
lineTop: l.top + window.scrollY,
|
||||||
|
lineBottom: l.bottom + window.scrollY,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const empty = measure();
|
||||||
|
line.textContent = "Password must be at least 12 characters.";
|
||||||
|
line.style.visibility = "visible";
|
||||||
|
const filled = measure();
|
||||||
|
line.textContent = "";
|
||||||
|
line.style.visibility = "hidden";
|
||||||
|
return { empty, filled };
|
||||||
|
}
|
||||||
|
|
||||||
|
test("the add wallet password error leaves Import where it was (#493)", async (env) => {
|
||||||
|
const page = await openPopup(env.ctx, env.popupUrl);
|
||||||
|
try {
|
||||||
|
await page.setViewportSize(POPUP_VIEWPORT);
|
||||||
|
// Brought up by toggling classes, as in the test above. The note
|
||||||
|
// addWallet.js shows once a wallet exists is the only part of the
|
||||||
|
// screen that differs between the first wallet and a later one.
|
||||||
|
await page.evaluate(() => {
|
||||||
|
const screen = document.getElementById("view-add-wallet");
|
||||||
|
for (const view of document.querySelectorAll(".view")) {
|
||||||
|
view.classList.toggle("hidden", view !== screen);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const walletExists of [false, true]) {
|
||||||
|
await page.evaluate(
|
||||||
|
(shown) =>
|
||||||
|
document
|
||||||
|
.getElementById("add-wallet-separate-password-note")
|
||||||
|
.classList.toggle("hidden", !shown),
|
||||||
|
walletExists,
|
||||||
|
);
|
||||||
|
for (const tab of ["tab-mnemonic", "tab-privkey", "tab-xprv"]) {
|
||||||
|
await page.click("#" + tab);
|
||||||
|
const { empty, filled } =
|
||||||
|
await page.evaluate(measureImportButton);
|
||||||
|
const where =
|
||||||
|
"#" +
|
||||||
|
tab +
|
||||||
|
(walletExists
|
||||||
|
? " with a wallet already added"
|
||||||
|
: " for the first wallet");
|
||||||
|
// Printed pass or fail, as the dust threshold test does.
|
||||||
|
console.log(
|
||||||
|
"# add wallet Import top, " +
|
||||||
|
where +
|
||||||
|
": " +
|
||||||
|
empty.buttonTop +
|
||||||
|
"px",
|
||||||
|
);
|
||||||
|
for (const m of [empty, filled]) {
|
||||||
|
assert(
|
||||||
|
m.lineTop >= m.buttonTop &&
|
||||||
|
m.lineBottom <= m.buttonBottom,
|
||||||
|
"the error line on " +
|
||||||
|
where +
|
||||||
|
" does not fit beside Import, so it adds height: " +
|
||||||
|
JSON.stringify(m),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert(
|
||||||
|
filled.buttonTop === empty.buttonTop,
|
||||||
|
"Import moved " +
|
||||||
|
(filled.buttonTop - empty.buttonTop) +
|
||||||
|
"px when the error appeared on " +
|
||||||
|
where,
|
||||||
|
);
|
||||||
|
if (!walletExists) {
|
||||||
|
assert(
|
||||||
|
empty.buttonTop < POPUP_VIEWPORT.height,
|
||||||
|
"Import starts at " +
|
||||||
|
empty.buttonTop +
|
||||||
|
"px on " +
|
||||||
|
where +
|
||||||
|
", below the fold",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// --------------------------------------------- confirmation screen (#238)
|
// --------------------------------------------- confirmation screen (#238)
|
||||||
//
|
//
|
||||||
// The screen that decides what gets signed. The arithmetic underneath it
|
// The screen that decides what gets signed. The arithmetic underneath it
|
||||||
|
|||||||
Reference in New Issue
Block a user