Skip to content

Commit 0f2de18

Browse files
v5 design: staged patch rollout (socket.yml + scan limit) (#290)
* Plan staged patch rollout for v5 Adds the design for rolling Socket patches out gradually: a `patches:` block in socket.yml that narrows what scan may patch (paths, ecosystems, packages, severity floor, on/off), and a severity-ordered per-run cap on new patches so each scan lands the next few most critical fixes. The plan splits the work into two parallel items with a frozen interface, lists every hard-coded filter and where it belongs, and covers the depscan autopatch follow-up. configuration.md now records that socket-patch reads socket.yml for selection policy only, with the trust boundary unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Revise rollout plan after adversarial review Three independent reviews (ambiguity, churn, trust boundary) found gaps that would have let the two implementations disagree or let a repo file widen or stall the rollout. The plan now: - matches paths against marker files with the backend's top-down gitignore rules, so projectIgnorePaths means the same everywhere - uses one data source for severity, supersession and ordering - spends the budget only on patches the planning pass proves can land, and admits nothing new when a lookup failed - uses the merged recorded view in every mode and engine - has depscan read the policy from the base commit for PR jobs - hardens file handling (regular files, aliases, size, encoding, trusted repo root) and reports what a policy hides Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Close interface gaps in the rollout plan A final consistency pass found places where the two work items would have produced incompatible code: base purls admitted in one directory being charged again in the next, no defined hand-off of the unfiltered offers from the severity filter to classification, no shared repo-relative path helper, and override sources the JSON must report but the interface could not carry. The shared contract now defines each of these, and the parity tests match each engine's budget scope. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Clarify who shapes scan's selection output The plan said both that the selector returns the shared offers struct (work item A) and that it returns admitted/deferred rows (work item B). The selector now returns the offers, and B adds the rollout stage after it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Apply socket.yml policy in memory path selection Bugbot on #290: the plan had the in-memory engine's path selector apply only the built-in test/fixture ignores, because it runs before socket.yml is read. A negation such as `!/e2e/tests/` could then never bring those trees back in depscan, while it works on disk. Selection is now two-phase: the caller fetches the root policy file(s) first and passes their text to selectHostedScanPaths, which applies the full path policy. A listed policy file that is not passed fails closed. A new memory test covers the negation case. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 388eea3 commit 0f2de18

3 files changed

Lines changed: 1089 additions & 14 deletions

File tree

‎docs/design/configuration.md‎

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Configuration design: env vars, the socket-cli config file, and what we deliberately don't read
22

3-
Status: **implemented** (v3.5). This document records the settled design so
4-
future configuration surface grows inside it instead of inventing new
5-
mechanisms.
3+
Status: **implemented** (v3.5); section 4 (`socket.yml` patch policy) is
4+
**planned** for v5.0 (see `staged-rollout.md`). This document records the
5+
settled design so future configuration surface grows inside it instead of
6+
inventing new mechanisms.
67

78
## Problem
89

@@ -69,35 +70,71 @@ UX policy and are ignored.
6970
- The python `socketsecurity` CLI already accepts `SOCKET_API_TOKEN`, so
7071
the canonical names are the cross-tool bridge; no `SOCKET_SECURITY_*`
7172
aliases were added.
72-
- `socket.yml` stays a scanning-product surface (projectIgnorePaths /
73-
issueRules / githubApp); socket-patch does not read it.
73+
- `socket.yml` is shared with the scanning product (projectIgnorePaths /
74+
triggerPaths / issueRules / githubApp). As of v5.0 socket-patch reads
75+
exactly two of its keys, `projectIgnorePaths` and a new `patches` block,
76+
and nothing else (section 4).
7477
- `SOCKET_PROXY_URL` (the public patch **endpoint**) must never be
7578
conflated with socket-cli's `apiProxy` (an HTTP **forward proxy**).
7679
Forward-proxy behavior comes from the standard
7780
`HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` vars, which reqwest honors.
7881

82+
### 4. `socket.yml` carries patch selection policy, never settings (v5.0)
83+
84+
Revisits the v3.5 position that socket-patch does not read `socket.yml`.
85+
Staged rollout needs a repo-owned, reviewable place to say which projects,
86+
ecosystems and packages may be patched, a severity floor and a per-run cap
87+
on new patches (`staged-rollout.md`). `socket.yml` is where Socket users
88+
already express repo policy, it lives at the repo root, and a new
89+
top-level `patches:` key is stripped or ignored by every existing parser.
90+
91+
The trust boundary is unchanged and gains its positive half:
92+
93+
- A repository file may **narrow or pace** what `scan` patches. It may
94+
never widen it, name an endpoint or credential, choose a mode or download
95+
format, or disable a safety interlock. The parser has no fields for any
96+
of those; such keys are unknown keys and fail validation.
97+
- Because the file only narrows, an unreadable file or an invalid
98+
`patches` block fails closed (exit 1, `socket_yml_invalid`, nothing
99+
written) instead of being treated as absent. (A repo with no `patches`
100+
block and a malformed `projectIgnorePaths` gets a warning, so repos that
101+
never opted in do not start failing.) This is the opposite of the socket-cli `config.json` rule above
102+
(corrupt → warn and ignore), and deliberately so: ignoring a broken
103+
user-level login file loses a convenience; ignoring a broken repo policy
104+
widens the rollout.
105+
- Lookup is bounded to the repository (nearest `.git` ancestor of
106+
`--cwd` owned by the user, honoring `GIT_CEILING_DIRECTORIES`, else
107+
`--cwd`), root files only, regular files only.
108+
- Flags and env vars still win over the file for scalars (CLI > env >
109+
file > default) and intersect with it for list filters;
110+
`--no-socket-yml` / `SOCKET_NO_SOCKET_YML` ignores the file.
111+
- Only `scan` (every mode) and the in-memory engine honor it. Commands
112+
that report, attest or undo existing state (`list`, `vex`, `rollback`,
113+
`remove`, `repair`, `apply`, `vendor`) ignore it; `get` bypasses it with
114+
a warning.
115+
79116
## Explicitly rejected
80117

81118
| Idea | Why not |
82119
|---|---|
83120
| Auto-loading `.env` / `.env.local` | Trust boundary: the tool mutates installed packages while holding an API token; a file in a *cloned repo* must never redirect endpoints, disable interlocks, or spend the token. Also the wrong convention class — npm/cargo/pip/git read no `.env`; dotenv is an app-runtime convention. Users who want it have direnv/mise/dotenvx. |
84121
| A new socket-patch config file (`.socket/config.toml`, …) | Duplicates socket-cli's persisted config; one more file format to trust, document, and migrate. |
85122
| Writing to socket-cli's `config.json` | No login flow here; shared mutable state and format drift for zero benefit. |
86-
| Honoring endpoints/credentials from repo-level files (manifest, socket.yml) | Same trust boundary as `.env`. Stated as a contract property in `CLI_CONTRACT.md`. |
123+
| Honoring endpoints/credentials/interlock switches from repo-level files (manifest, socket.yml) | Same trust boundary as `.env`. Stated as a contract property in `CLI_CONTRACT.md`. Selection policy that only narrows is the one exception (section 4). |
124+
| A `version: 3` socket.yml for the `patches` block | socket-cli rejects any version but 2; older ajv parsers would treat 3 as 2 anyway. The block is additive under `version: 2`. |
125+
| Per-directory `socket.yml` files | No existing consumer supports them; one root file with `includePaths` covers monorepos. |
87126
| `SOCKET_CLI_CONFIG` (ephemeral full-JSON config override) | Imports socket-cli's whole config vocabulary as a permanent compat contract. |
88127
| Mapping `apiProxy` → anything | Forward-proxy vs patch-endpoint semantic trap; `HTTP_PROXY` et al. already work. |
89128
| `enforcedOrgs` / `skipAskToPersistDefaultOrg` | Interactive socket-cli UX policy with no socket-patch analog. |
90129

91130
## Deferred (designated homes, no implementation yet)
92131

93-
- **Project-level behavioral defaults** (`ecosystems`, `downloadMode`,
94-
`vendorSource`): if demand materializes, they go in the manifest `setup`
95-
block (`setup.defaults`, camelCase) — the manifest already controls what
96-
gets patched, so behavioral defaults there grant no new capability, and
97-
the serde struct simply has no fields for URLs/credentials/interlocks.
98-
Requires teaching the TS zod twin
99-
(`npm/socket-patch/src/schema/manifest-schema.ts`) to model `setup`.
100-
Precedence would be flag > env > `setup.defaults` > default.
132+
- **Project-level behavioral defaults** (`downloadMode`, `vendorSource`,
133+
mode): not planned. The v3.5 idea of a manifest `setup.defaults` block is
134+
obsolete in v5 (hosted and vendored projects have no manifest and `setup`
135+
is removed). Selection policy (ecosystems, packages, paths, severity,
136+
per-run cap) went to `socket.yml` `patches` instead (section 4). Anything
137+
that is not pure narrowing stays out of repo files.
101138
- **Env cleanup sweep**: core's direct env readers (`SOCKET_OFFLINE` in
102139
`utils/env_compat.rs`, `SOCKET_TELEMETRY_DISABLED` in `telemetry.rs`)
103140
still match only `1|true`, unlike `parse_bool_flag`'s vocabulary (the CLI

0 commit comments

Comments
 (0)