mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
The picker behind Link Existing's "Browse" and the mobile keyboard's Path key refused every path with a dot-prefixed segment, so `.github/workflows/ci.yml` could not be selected and a hidden folder could not be opened at all. It gains the same `.*` toggle as the File Viewer: default OFF, per-device, and applied to both the listing and the preview endpoint, which re-resolves the path independently. That dotfile filter was quietly doing security work. The picker's roots include Home, so with every hidden path unreachable the shared blocklist never had to name the credentials that live in dot-directories. Lifting the filter removes that accident, so `isSensitivePath` now covers them explicitly: SSH keys at any depth rather than only under $HOME, GPG keyrings, AWS/GCloud/Azure/Docker/ Kubernetes credentials, npm, Yarn, git, gh, netrc, PyPI, RubyGems, Cargo and Terraform tokens, .pgpass and .my.cnf, and the Claude and Codeman agent credentials. `~/.codeman/` and `~/.claude/` stay attachable as trees, since the publish skill and the review-card loop read from them; only their secret-bearing members are named. Everything else still applies with the toggle on: blocked trees, sensitive files, root confinement, ownership scoping and symlink-escape checks. A hidden entry whose realpath is a secret is dropped from the listing, and opening it is refused. Follows #221 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
92 lines
3.6 KiB
TypeScript
92 lines
3.6 KiB
TypeScript
/**
|
|
* @fileoverview Shared sensitive-path blocklist.
|
|
*
|
|
* A small defense-in-depth blocklist of absolute paths that must never be
|
|
* served to the browser regardless of how the path was obtained (workspace
|
|
* download, cross-workspace attachment registration, raw/preview serving).
|
|
*
|
|
* This is intentionally a BLOCKLIST, not a workspace-confinement check:
|
|
* cross-workspace attachment is a supported feature (codeman-publish skill +
|
|
* the automated review-card loop attaching files under ~/.codeman/), so a
|
|
* strict session-workspace boundary would break legitimate use. The blocklist
|
|
* rejects well-known secret locations (system password files, SSH keys, cloud
|
|
* credentials, dotenv files) while leaving ordinary cross-workspace files
|
|
* attachable.
|
|
*
|
|
* ⚠️ The path picker's `showHidden` option is what makes the dot-prefixed half
|
|
* of this list load-bearing. Before it existed, the picker refused every path
|
|
* with a hidden segment, so `~/.config/gh/hosts.yml` and friends were
|
|
* unreachable by construction and the list only had to cover the few secrets
|
|
* that live in plain sight. Opting into hidden entries removes that accident,
|
|
* so every credential location below has to be named. Adding a new browse
|
|
* surface means re-reading this file, not assuming it already covers you.
|
|
*
|
|
* ⚠️ Deliberately NOT whole-tree blocks: `~/.codeman/` (the publish skill
|
|
* attaches from it) and `~/.claude/` (transcripts and team state are ordinary
|
|
* files worth attaching). Only their secret-bearing members are named.
|
|
*
|
|
* Callers MUST resolve symlinks (realpath) BEFORE calling isSensitivePath so a
|
|
* symlink pointing at a sensitive target is also caught.
|
|
*/
|
|
|
|
const SENSITIVE_PATTERNS: RegExp[] = [
|
|
// System account databases.
|
|
/^\/etc\/shadow$/,
|
|
/^\/etc\/gshadow$/,
|
|
/^\/etc\/master\.passwd$/,
|
|
|
|
// SSH and GPG private key material. `.ssh/` is matched at any depth rather
|
|
// than only under homedir(): a per-project or per-deploy key directory holds
|
|
// exactly the same secret, and it drops a homedir() read that is captured at
|
|
// module load and therefore wrong for anything that changes HOME later.
|
|
/\/\.ssh\//,
|
|
/\/\.gnupg\//,
|
|
|
|
// Dotenv, in every conventional spelling (.env, .env.local, .env.production).
|
|
/\/\.env$/,
|
|
/\/\.env\./,
|
|
|
|
// Generic credential files, plus the per-vendor spellings that do not match it.
|
|
/\/credentials(\.json|\.yml|\.yaml|\.xml|\.toml|\.db)?$/i,
|
|
/\/\.aws\/(credentials|config)$/,
|
|
/\/\.aws\/sso\/cache\//,
|
|
/\/\.gcloud\/credentials\.db$/,
|
|
/\/\.config\/gcloud\//,
|
|
/\/\.azure\//,
|
|
/\/\.docker\/config\.json$/,
|
|
/\/\.kube\/config$/,
|
|
|
|
// Package-registry and forge tokens. Each of these is a bearer credential in
|
|
// a plain-text dotfile, which is exactly what a path picker will surface.
|
|
/\/\.npmrc$/,
|
|
/\/\.yarnrc\.yml$/,
|
|
/\/\.git-credentials$/,
|
|
/\/\.config\/gh\//,
|
|
/\/\.config\/hub$/,
|
|
/\/\.netrc$/,
|
|
/\/_netrc$/,
|
|
/\/\.pypirc$/,
|
|
/\/\.gem\/credentials$/,
|
|
/\/\.cargo\/credentials(\.toml)?$/,
|
|
/\/\.terraformrc$/,
|
|
/\/\.terraform\.d\//,
|
|
|
|
// Database client credentials.
|
|
/\/\.pgpass$/,
|
|
/\/\.my\.cnf$/,
|
|
|
|
// Agent CLI credentials, including Codeman's own hook secret and user table.
|
|
// Named individually so the surrounding trees stay attachable (see above).
|
|
/\/\.claude\/\.credentials\.json$/,
|
|
/\/\.codeman[^/]*\/hook-secret$/,
|
|
/\/\.codeman[^/]*\/users\.json$/,
|
|
];
|
|
|
|
/**
|
|
* Returns true if the given ABSOLUTE, symlink-resolved path matches the
|
|
* sensitive-file blocklist and must not be served to the browser.
|
|
*/
|
|
export function isSensitivePath(absPath: string): boolean {
|
|
return SENSITIVE_PATTERNS.some((pattern) => pattern.test(absPath));
|
|
}
|