|
|
|
@@ -0,0 +1,115 @@
|
|
|
|
|
# Row 243: too-open mode refusal tests set their mode with chmod
|
|
|
|
|
|
|
|
|
|
Brief: `docs/plans/2026-10-21_gate-env-rows.md` § "The startup token refusal test sets its file mode"
|
|
|
|
|
(Sage, sage-1909). Reviewer Webby. Lands after row 197 (mos-1162). Design notes written before 197
|
|
|
|
|
lands, read at `origin/refactor` 01e1fe512. Recheck the line cites against 197's landing before code
|
|
|
|
|
starts.
|
|
|
|
|
|
|
|
|
|
## The defect
|
|
|
|
|
|
|
|
|
|
`writeFileSync(file, data, { mode })` and `mkdirSync(dir, { mode })` apply the process umask to the
|
|
|
|
|
mode. A test that creates a "too open" file this way and expects a refusal passes under `umask 022`
|
|
|
|
|
(0644 stays 0644) and fails under `umask 077` (0644 becomes 0600, and the source accepts it).
|
|
|
|
|
|
|
|
|
|
Row 197 r6 (df0c59608) runs each gate step under `umask 022` (`gate-lane.mjs:46`), so the lane no
|
|
|
|
|
longer hits this. Row 243 still makes the tests independent of the umask, as the brief asks.
|
|
|
|
|
|
|
|
|
|
## Sweep
|
|
|
|
|
|
|
|
|
|
Method: every source-side mode check in `packages/*/src` and `scripts/` (grep for `mode & 0o...`), then
|
|
|
|
|
the tests that reach each check's refusal branch, deciding whether the too-open object gets its mode at
|
|
|
|
|
creation or from an explicit chmod. Also a scan of every `writeFileSync`/`mkdirSync`/`openSync` in
|
|
|
|
|
`packages/*/tests` with a group or other bit in its mode (50 hits, 41 without a following chmod), and a
|
|
|
|
|
grep of the refusal messages for objects created with no mode or by a shell-out. Read at
|
|
|
|
|
`origin/refactor` with `git show` and `git grep`; nothing run.
|
|
|
|
|
|
|
|
|
|
Affected refusal tests, all three verified by reading the test (the fourth file, an accept test, follows the table):
|
|
|
|
|
|
|
|
|
|
| Creation | Test | Source check | Expected refusal |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `packages/bus/tests/process.test.mjs:76` `writeFileSync(file, secret, { mode: 0o644 })` | startup token refusal returns safe code... | `bus/src/credentials.mjs:205` (must be 0600) | `credential-file`, exit 2 (:83-84) |
|
|
|
|
|
| `packages/webui/tests/proof-cli.test.mjs:28` `writeFileSync(file, value, { mode })`, mode 0o644 from the `mode` case at :27 | proof startup refuses partial flags... | `webui/src/serve.mjs:234` (0600 or 0400) | status 2 (:38) |
|
|
|
|
|
| `packages/webui/tests/sso.test.mjs:360` `writeFileSync(.../secrets/mosaic-stack/a, ..., { mode: 0o644 })` | 11: the CLI refuses --sso... | `webui/src/sso.mjs:45` (no group or other bits) | status 2 and the "no group or other permission bits" line (:361-363); the chmod to 0600 at :364 comes after |
|
|
|
|
|
|
|
|
|
|
Fix for each: keep the write, then `chmodSync(<path>, 0o644)` right after it. For `proof-cli.test.mjs`
|
|
|
|
|
the chmod goes inside the case map after the write, with the case's own mode, so the 0o600 cases are set
|
|
|
|
|
explicitly too.
|
|
|
|
|
|
|
|
|
|
Not affected (each sets the mode with a chmod or a chmodding helper before the code under test reads it):
|
|
|
|
|
bus credentials.test :52, credential-store.test :55-61; business access.test :225-226, business.test
|
|
|
|
|
:159/161/174, credentials.test :60, cli.test :151, helpers `writeJson`/`tokenFile`; cli cli.test :176,
|
|
|
|
|
notifier.test :318/:321, stack-release.test :1220/1239/1294/1313/1552, stack-upgrade.test :426-430,
|
|
|
|
|
case2-unit.test :340/:510/:635; control-board discord.test :79; discord binding.test :106/118/140/277,
|
|
|
|
|
notify.test :76, setspark.test :110, git.test :138/:360, offline-check.test :21; mosaic safety.test :80;
|
|
|
|
|
queue review.test :214/:228; ledger gitea-helper-raw.test :113; webui service.test :797, sso.test :310;
|
|
|
|
|
bus host-tools.test :182/:266. The 0o644/0o755 adapter and fake-binary writes (launcher, thread-launch,
|
|
|
|
|
session, sandbox, checkout, commit, etc.) aren't read by a mode check, or are only checked for the exec
|
|
|
|
|
bit, which 0700 keeps. `host-tools.test.mjs:205`, `:271` and the confDir at `:77` expect acceptance and
|
|
|
|
|
only need "not group-writable".
|
|
|
|
|
|
|
|
|
|
Mode checks with no refusal test at all (not this row; listed for the record): `bus/src/store.mjs:121`,
|
|
|
|
|
`bus/src/host-run.mjs:71`, `business/src/organization.mjs:28/45/109/141`,
|
|
|
|
|
`conversation/src/controller.mjs:299`, `mosaic/src/materialize-fixture.mjs:16`,
|
|
|
|
|
`mosaic/src/refresh-fixture.mjs:64`, `seat/src/session.mjs:72`, `scripts/git-credential-mosaic.mjs:49`,
|
|
|
|
|
and the mode branch of `scripts/stack-release.sh:1952`.
|
|
|
|
|
|
|
|
|
|
Fourth file, folded in by Sage (sage-1918): `packages/cli/tests/stack-instances.test.mjs:84` and `:89`
|
|
|
|
|
assert 0644 on the config and `instance.json`. `scripts/mosaic-config.mjs:337` creates them with
|
|
|
|
|
`openSync(file, "wx", 0o644)` and no chmod, so under `umask 077` they come out 0600 and the test fails.
|
|
|
|
|
This is an accept test of a source mode, not a refusal test.
|
|
|
|
|
|
|
|
|
|
| Creation | Test | Source | Ruling |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `scripts/mosaic-config.mjs:337` via `bootstrap()` (`stack-instances.test.mjs:41`, a `spawnSync` of `scripts/bootstrap.sh`) | the `--instance` create test, :79-89 | `createExclusive` passes 0o644 and the umask narrows it | test-only (sage-1918) |
|
|
|
|
|
|
|
|
|
|
- No change to `mosaic-config.mjs`. Letting the operator's umask narrow the config is the source's
|
|
|
|
|
choice; a chmod to 0644 would make the config wider than that umask. No row asks for it.
|
|
|
|
|
- `bootstrap()` runs its child under an explicit `umask 022`, the pattern from `gate-lane.mjs:46`:
|
|
|
|
|
`run("/bin/bash", ["-c", 'umask 022; exec "$@"', "bash", BOOTSTRAP, ...args], homeEnv(home))`. Every
|
|
|
|
|
`bootstrap()` call goes through it (:79, :99, :102, :119, :127, :136, :140, :152, :165, :169), so the
|
|
|
|
|
0644 assertions still prove the mode the source asks for, whatever the gate's umask.
|
|
|
|
|
- New assertion: a `bootstrap --instance` run under `umask 077` makes the config and `instance.json`
|
|
|
|
|
0600. That pins that the source honors the umask, so a later chmod "fix" fails a test. The directories
|
|
|
|
|
stay 0700 in both runs (they're created with `mode: 0o700`, which 077 can't narrow).
|
|
|
|
|
- The `config()` helper (:42) has no mode assertion and stays as it is.
|
|
|
|
|
|
|
|
|
|
Tests outside `packages/*/tests`: the five `scripts/foundation/*.test.mjs` files have no open-mode
|
|
|
|
|
write. `cli.test.mjs:144/148` chmods to 0o000 and 0o600, which doesn't depend on the umask. The
|
|
|
|
|
`agents/*/work` probe tests are review evidence, not gate steps (`discoverSteps` doesn't run them), so
|
|
|
|
|
they're out of scope.
|
|
|
|
|
|
|
|
|
|
## Tests
|
|
|
|
|
|
|
|
|
|
- Each of the four files runs whole twice, once under `umask 022` and once under `umask 077`
|
|
|
|
|
(`bash -c 'umask 077; node --test <file>'`). Both pass. The brief requires this for process.test.mjs;
|
|
|
|
|
Sage extended it to all four (sage-1918).
|
|
|
|
|
- Proof that the change matters: before the change, the same four files under `umask 077` fail on the
|
|
|
|
|
named assertions. I record that run too, on the slot, so the 077 pass after the change is meaningful.
|
|
|
|
|
- Order (mos-1162): this lands after 197 r6, so r6's proof ran on the unchanged test.
|
|
|
|
|
|
|
|
|
|
## Files (expected)
|
|
|
|
|
|
|
|
|
|
`packages/bus/tests/process.test.mjs`, `packages/webui/tests/proof-cli.test.mjs`,
|
|
|
|
|
`packages/webui/tests/sso.test.mjs`, `packages/cli/tests/stack-instances.test.mjs`. Test-only. No
|
|
|
|
|
source change.
|
|
|
|
|
|
|
|
|
|
## Build (sage-2056: start on row 197's landing a36523713)
|
|
|
|
|
|
|
|
|
|
Branch `morgana/row243`, one commit on a36523713. Cites rechecked there: all hold except proof-cli's
|
|
|
|
|
write, which is now `proof-cli.test.mjs:30` (was :28).
|
|
|
|
|
|
|
|
|
|
- `bus/tests/process.test.mjs`: imports `chmodSync`; `chmodSync(file, 0o644)` after the write at :76.
|
|
|
|
|
- `webui/tests/proof-cli.test.mjs:30`: `chmodSync(file, mode)` after the write, with each case's own
|
|
|
|
|
mode, so the 0o600 cases are set explicitly too.
|
|
|
|
|
- `webui/tests/sso.test.mjs:360`: `chmodSync(..., 0o644)` after the write, before the `--sso` start
|
|
|
|
|
that must refuse.
|
|
|
|
|
- `cli/tests/stack-instances.test.mjs`: `underUmask(mask, home, args)` runs `scripts/bootstrap.sh`
|
|
|
|
|
through `/bin/bash -c 'umask <mask>; exec "$@"'`; `bootstrap()` is `underUmask("022", ...)`. New
|
|
|
|
|
subtest in T-I1: under umask 077 the config dir and root are 0700, and the config and `instance.json`
|
|
|
|
|
are 0600.
|
|
|
|
|
- Hash pins: no test or script names any of the four files (a grep for the basenames; positive control
|
|
|
|
|
`stack-release.test` found in two files). `stack-release.test.mjs:2122` mentions
|
|
|
|
|
`stack-instances.test.mjs` only in a comment.
|
|
|
|
|
|
|
|
|
|
Tests: not run yet. They wait for Sage's slot "clear" (sage-2056). Plan per file: the base (a36523713)
|
|
|
|
|
under umask 077 (expected to fail), then the candidate under 022 and 077.
|