Aug 26 - Update password detect against off field
CI / Python lint (flake8) (push) Has been cancelled
CI / Python syntax check (push) Has been cancelled
CI / Alembic migration chain (push) Has been cancelled
CI / JavaScript syntax check (push) Has been cancelled
CI / Pytest (push) Has been cancelled
CI / Build extension zip (push) Has been cancelled

This commit is contained in:
2026-08-26 14:47:16 -04:00
parent b84a6d9245
commit 2f1afb143c
4 changed files with 287 additions and 40 deletions
+31 -2
View File
@@ -121,7 +121,9 @@ passkeeper/
│ ├── test_registration_privacy.py # register does not disclose account existence
│ ├── test_emergency_visibility.py # grantor sees requests + retrievals
│ ├── test_deploy_config.py # nginx/gunicorn/systemd/extension packaging guards
│ └── js/test_psl.js # PSL same-site matching (node, run in CI)
│ └── js/
│ ├── test_psl.js # PSL same-site matching (node, run in CI)
│ └── test_field_heuristics.js # login-field detection predicates
├── gunicorn.conf.py # worker class, timeouts, preload_app=False
├── pytest.ini
├── requirements-dev.txt
@@ -618,7 +620,32 @@ In both `content.js` and `popup.js`. Prevents silent match failures for bare dom
1. **YES:** `autocomplete="username|email|tel"`
2. **NO:** non-credential autocomplete (`name`, `organization`, `search`, etc.)
3. **YES:** `name/id/placeholder/aria-label` matches `user|email|mail|login|phone|tel|mobile|account`
4. **Otherwise:** not decorated
4. **Then:** `_hasPasswordSibling()` must also pass
5. **Otherwise:** not decorated
**`autocomplete="off"` is NOT a negative signal** and must never be added back to
`NON_CRED_AC`. Routers, banks and admin panels set it on login fields precisely
to discourage password managers. It previously caused step 2 to reject fields as
obvious as `<input id="login_username" placeholder="Username" autocomplete="off">`
before step 3 ever ran (ASUS RT-AX88U admin login). Letting it fall through is
safe — the field still needs a credential keyword AND a nearby password input.
### Credential capture without a `<form>`
Many login UIs never use a `<form>` — the ASUS router admin page submits with
`<div class="button" onclick="preLogin();">Sign In</div>`, so no `submit` event
is ever dispatched and the save-credentials banner never appeared.
`watchSubmissions()` therefore registers three triggers, all routed through
`maybeCaptureCredentials(scope)`:
1. `submit` on any form (scope = the form)
2. `click` on anything `_looksLikeSubmitControl()` accepts — `<button>`,
`input[type=submit|button|image]`, `[role=button]`, any element with an
inline `onclick`, or a button-ish class name (scope = document)
3. `Enter` keydown inside a password or likely-username field (scope = document)
`_captureCooldown` (2 s) prevents two triggers double-prompting for one login.
### MutationObserver guard
@@ -663,6 +690,8 @@ Audit log details **never** contain plaintext item names, shared item names, or
- `/register` must return the SAME body and status for new and existing addresses, and hash on both paths — returning early on duplicate reinstates a timing oracle
- Never return `str(e)` from exception handlers — log with `_log.exception(...)` and return a generic user-facing message to avoid leaking DB schema details or query fragments
- `extension/shared/psl.js` is GENERATED — never hand-edit; run `python scripts/update_psl.py`. It must load BEFORE content.js / popup.js / background.js in every manifest
- Never add `off` back to `NON_CRED_AC` in `content.js``tests/js/test_field_heuristics.js` fails the build if you do
- Login detection must not assume a `<form>` exists; route new capture triggers through `maybeCaptureCredentials()`
- Never reintroduce `endsWith("." + host)` host matching anywhere in the extension — `tests/test_deploy_config.py` fails the build if it reappears
- nginx rate zones: mind `r/s` vs `r/m`. `api_limit` was `60r/m` (1 req/s for the whole API) and caused spurious 429s on normal vault use
- `preload_app` must stay `False` in `gunicorn.conf.py` — APScheduler's thread does not survive `fork()`, so `--preload` silently disables the cleanup job