Oats security
Use when changing oats code that touches auth/OAuth, session tokens, the capabilities allowlist, a Tauri invoke command, file paths, URL/deep-link opening, the sidecar, network calls, or the offline-mode privacy guarantee. Encodes this app's real attack surface and a pre-PR review checklist.From its SKILL.md
npx -y skills add ariso-ai/oats --skill oats-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 23 stars23 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.0 KB, as published. Nobody here has run it
oats Security Review
oats records meetings (highly sensitive audio + transcripts), holds an auth session, and
runs a native sidecar — so changes near the surfaces below deserve a deliberate security
pass. Use this alongside superpowers' requesting-code-review and the built-in
/security-review command. Default to suspicion: if a change touches a surface here,
walk its checklist before opening the PR.
The attack surfaces (where they live)
- Tauri
invokeboundary — every#[tauri::command]insrc-tauri/src/commands.rsis reachable from any loaded webview (incl. the external OAuth page). Treat command arguments as untrusted input. - File paths from the frontend — commands like
read_recording_audio/ note read-write take a path/id and hitstd::fs::read/read_to_string/create_dir_all. Path-traversal risk: a malicious id (../../) could escape~/.ariso/recordings/. Confirm ids are validated and paths are built fromstorage::recordings_dir(...), never concatenated from caller-supplied absolute paths. - Auth / OAuth —
auth.googleSignIn→/oauth2/prepare-state(CSRF state) → the default browser (system opener), with the magic-link token returned to a127.0.0.1loopback listener (RFC 8252) and the result delivered via theoauth-resultevent. Verify: state is generated server-side and checked; the loopback listener binds loopback only and rejects callbacks without the per-attempt nonce; the opened URL is scheme-validated; the returnedsessionTokenis never logged or emitted to other windows. - Session token storage — tokens/Bearer creds back the
http_client()(AUTHORIZATIONheader). The@tauri-apps/plugin-store(settings.json) is plaintext on disk — fine for prefs (backend,onboarded), not for secrets. Confirm no token/secret is written to plugin-store; secrets belong in the OS keychain or backend-managed session. - Capabilities allowlist —
src-tauri/capabilities/default.jsonis least-privilege and should stay that way. Note the scopedopener:allow-open-url(onlyx-apple.systempreferences:*). Don't broaden a permission or add a window without justifying it; never add a wildcard URL/open scope. - URL / deep-link opening —
app.opener().open_url(...)and notification deep links (meeting_notifications.rscarries a URL as the request id). Only open URLs whose origin you control (theweb.ari.ariso.aiweb app). Validate scheme + host before opening anything derived from server data or a notification payload. - Sidecar execution —
ariso-sttis spawned with--audio <path>etc. It usesCommand::new+.arg(...)(no shell), so keep it that way — never route sidecar args through a shell string or interpolate untrusted data into one. - Network / API base —
DEFAULT_API_BASE_URLis compiled per feature; release builds intentionally ignore env overrides (ARISO_DESKTOP_API_BASE_URL). Don't add a runtime override path that would let an attacker repoint the API. Keephttps://in non-local builds;http://localhostis dev-only. - Updater — R2-hosted, signature-verified with the public key. Updates must stay signature-verified; never disable the pubkey check or pull updates over plain HTTP.
- Offline-mode privacy invariant — when the Local backend is selected, nothing
leaves the machine. Any new network call (
reqwest, telemetry, fetch) on a code path reachable in Local mode breaks the product's core promise. Gate network I/O behind the cloud backend explicitly. - Sensitive data in logs — transcripts, audio paths, tokens, and meeting content must not land in stdout/stderr/log files. The sidecar contract is "stdout = result, stderr = logs" — keep secrets out of both.
Pre-PR checklist
For a change touching any surface above, confirm:
- New/changed
invokecommand validates and bounds every argument (esp. paths/ids). - No secret (token, key) written to plugin-store or any plaintext file; none logged.
- No capability/permission/window added or widened without a written reason; no wildcard URL open scope.
- Any opened URL/deep link is origin- and scheme-checked against a trusted host.
- No new network call reachable in Local (offline) mode.
- Sidecar args stay argv-based (no shell), no untrusted interpolation.
- Updater signature verification and HTTPS endpoints untouched.
- Ran
/security-review(or requested review) on the diff.
When this surfaces a real finding, fix it via oats-tauri (backend) / oats-vue
(frontend) and re-verify. For runtime reproduction, use oats-debugging.