Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
日本語の概要は準備中です。原文の説明を表示しています。
Use when the diff adds or changes a loopback HTTP or WebSocket server, a bind address, a Unix socket, named pipe, XPC or D-Bus service, an Electron or Tauri main-process handler, a custom URL scheme or file association, a privileged helper or updater, or code that spawns tools inside directories a user or download supplied
インストールする前に、エージェントに与えられる指示の中身を確認できます。
A desktop app's attackers are not "the internet" in general. They are a web page open in the user's browser (which can send requests to 127.0.0.1), another app or OS user on the same machine, a document or archive the user opened, a link the user clicked, and remote content loaded into a webview. Each reaches the app through a different door: a loopback port, a socket, a URL scheme, an IPC channel, a file.
Core principle: name the attacker's starting position, then show the check that stops it at that door. A process name, a path, a port number or a claimed sender field is not authentication — an OS peer credential, a code-signature check, or an unguessable per-launch token is.
Any web page can send requests to http://127.0.0.1:<port>. "Simple" requests — a form POST, text/plain bodies, GET with side effects — need no CORS preflight and still execute; DNS rebinding lets a page on attacker.example read responses once its name resolves to 127.0.0.1; a WebSocket upgrade carries no CORS check at all.
// ❌ a local API that runs agent tasks, open to every page in every browser on the machine
mux.HandleFunc("/api/run", runHandler)
http.ListenAndServe("127.0.0.1:"+port, mux)
// ✅ a per-launch random token in a header (pages cannot read or guess it, and it is never a cookie),
// plus a Host check that defeats DNS rebinding for the routes that need no token
func guard(next http.Handler, token string, port string) http.Handler {
allowed := map[string]bool{"127.0.0.1:" + port: true, "localhost:" + port: true}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !allowed[r.Host] { http.Error(w, "bad host", http.StatusForbidden); return }
if subtle.ConstantTimeCompare([]byte(r.Header.Get("Authorization")), []byte("Bearer "+token)) != 1 {
http.Error(w, "unauthorized", http.StatusUnauthorized); return
}
next.ServeHTTP(w, r)
})
}
Findings: a new local route outside the token middleware that changes state or returns private data; authentication by cookie on a loopback API (CSRF from any page); CORS on the local server reflecting any origin; a WebSocket endpoint that skips the token or the Origin check; the token placed in a URL query string that lands in logs or browser history.
":8080", "0.0.0.0" and "[::]" listen on every interface — the LAN, a café Wi-Fi, a VPN. A local-only service must bind 127.0.0.1 (or ::1). Go's http.ListenAndServe(":"+port, …), Node's server.listen(port) with no host and Python's host="0.0.0.0" all expose it.
/tmp) with a predictable name, or with mode 0777/a permissive umask — another OS user connects or pre-creates the path. Check SO_PEERCRED/getpeereid when the service acts for a specific user.PIPE_REJECT_REMOTE_CLIENTS, or a pipe name another process can create first (squatting).Renderer-side settings are in web-frontend-security §7; the main process is where privilege lives.
// ❌ any frame — including remote content or an injected iframe — can ask the main process to run a command
ipcMain.handle("run", (_e, cmd: string, args: string[]) => execFile(cmd, args));
// ✅ check the sender's origin on every call, and expose an operation, not a primitive
ipcMain.handle("open-project", (e, id: string) => {
if (new URL(e.senderFrame.url).origin !== "app://tasktrooper") throw new Error("forbidden");
return projects.open(validateId(id));
});
Also: shell.openExternal(url) without a scheme allowlist (file:, smb:, custom schemes launch other programs); missing setWindowOpenHandler and will-navigate handlers, so a link navigates the privileged window to remote content; a custom protocol handler (protocol.handle) that serves files by joining a path from the URL (path-traversal-and-file-handling); Tauri commands with broad fs/shell scopes reachable from remote content.
A custom scheme (tasktrooper://), a file association or a share target can be fired by any web page, email or file. Treat everything it carries as untrusted:
state and PKCE verifier of a flow this app started;second-instance or open-url passed on to a child process or parsed for switches — a URL can carry --flag-shaped text; put -- before operands and parse the URL, not the argv string.A directory the user did not author — an extracted archive, a downloaded workspace, a cloned third-party repo — carries configuration that tools execute: .git/config (core.fsmonitor, core.sshCommand, core.hooksPath), .npmrc, .envrc, Makefile, .vscode/tasks.json, a pyproject.toml build backend. Code that runs git status, npm, make or a language server inside such a directory without isolation runs the directory's code. Also: resolving a bare program name through a PATH that includes the working directory.
0644 in a shared location, a world-readable temp file).ps to every local user) — pass them by environment or file descriptor.Any web page reaching command execution, file read or account actions through a loopback server, URL scheme or IPC → CRITICAL. A service exposed to the LAN without authentication → HIGH. Another local user reading credentials or driving a privileged helper → HIGH. A secret on argv → MEDIUM.
127.0.0.1 means "only this app can call it" — browsers can, and so can every local process.contextIsolation: true as protection for an ipcMain handler that executes whatever the renderer sends.listen(":port"), 0.0.0.0 or [::] on a local-only service.ipcMain.handle/ipcMain.on that never reads event.senderFrame, and passes arguments to exec, spawn, fs or shell.openExternal.setAsDefaultProtocolClient or open-url handling that performs an action directly.git, npm, make or a build tool spawned with cwd set to an extracted or downloaded directory.Electron security checklist · Tauri security · OWASP Desktop App Security Top 10 · CWE-214, 306, 346, 427, 668, 1385 · Cloudflare security-audit-skill
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
日本語の概要は準備中です。原文の説明を表示しています。
Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping
日本語の概要は準備中です。原文の説明を表示しています。
Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal
日本語の概要は準備中です。原文の説明を表示しています。
Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task
日本語の概要は準備中です。原文の説明を表示しています。
How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.
日本語の概要は準備中です。原文の説明を表示しています。
Use when deciding whether a request needs an analiz task before implementation - the conditions that require the architect's analysis versus going straight to implementation
日本語の概要は準備中です。原文の説明を表示しています。