stack-release: round 2, slot and proof checks resolve REPO, config_checks refuses a config that doesn't parse (row 200, #1698, sage-1432)
This commit is contained in:
1 parent
d163cfccb5
commit
e3fd9f3a08
5 files changed
+85
-17
No files matched your search
@@ -3,13 +3,13 @@
|
|||||||
Launchpad, 2026-10-20. Author Launchpad, reviewer Webby, gate owner Sage. Issue #1698. Brief
|
Launchpad, 2026-10-20. Author Launchpad, reviewer Webby, gate owner Sage. Issue #1698. Brief
|
||||||
`docs/plans/2026-10-20_stack-release-sso.md` (blob 426336b177f6). Rulings sage-1389 (R1 uses the activation onto
|
`docs/plans/2026-10-20_stack-release-sso.md` (blob 426336b177f6). Rulings sage-1389 (R1 uses the activation onto
|
||||||
the current copy, plus a same-copy test and a latest-set mutant), sage-1403 and sage-1407 (N4), mos-1056 item 5
|
the current copy, plus a same-copy test and a latest-set mutant), sage-1403 and sage-1407 (N4), mos-1056 item 5
|
||||||
(N4 is a window acceptance item).
|
(N4 is a window acceptance item), sage-1432 (round 2 for webby-0028 N1 and N2).
|
||||||
|
|
||||||
Base 4d487dfdc (`base.txt`): aa1bd209e plus row 192's two commits picked. 48359dbfa is 83b32d8d8 (patch-id
|
Base 4d487dfdc (`base.txt`): aa1bd209e plus row 192's two commits picked. 48359dbfa is 83b32d8d8 (patch-id
|
||||||
8d11f48a9e03) and 4d487dfdc is r2 37582e820 (patch-id 0e32af8aa2a4 on both). The webui tree at the base matches
|
8d11f48a9e03) and 4d487dfdc is r2 37582e820 (patch-id 0e32af8aa2a4 on both). The webui tree at the base matches
|
||||||
`launchpad/row192` at 37582e820 path for path. Candidate: commit 5dc0b5534 on `launchpad/row200`.
|
`launchpad/row192` at 37582e820 path for path. Candidate: r1 5dc0b5534 and the round 2 commit on `launchpad/row200`.
|
||||||
`candidate-manifest.sha256` lists its 3 paths; the packet files are not in it. Drift check at commit time:
|
`candidate-manifest.sha256` lists its 3 paths; the packet files are not in it. Drift check at commit time:
|
||||||
`git log aa1bd209e..origin/refactor` (origin/refactor ae3c36f24) touches none of the 3 paths.
|
`git log aa1bd209e..origin/refactor` (origin/refactor 9ba31b5b3 at round 2) touches none of the 3 paths.
|
||||||
|
|
||||||
No test touches `~/.mosaic-dev`, `~/.config/mosaic-dev`, `~/.mosaic`, a live unit or a live slot. T56 runs on
|
No test touches `~/.mosaic-dev`, `~/.config/mosaic-dev`, `~/.mosaic`, a live unit or a live slot. T56 runs on
|
||||||
the scratch roots and seeded pointers rows 187 and 189 use. Its config and slot sit in the test's temp dir,
|
the scratch roots and seeded pointers rows 187 and 189 use. Its config and slot sit in the test's temp dir,
|
||||||
@@ -20,7 +20,7 @@ and the slot carries the `proofFile` read watch, so a run that opened it would f
|
|||||||
| Path | Change |
|
| Path | Change |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `scripts/stack-release.sh` | `--sso` on activate (usage, parse, `sso_for_target`), `sso_checks` and `slot_problem` (item 2), `sso_base` (rollback flags, item 3), `config_checks` (item 3 and N4), the `sso:` plan line and step lines. |
|
| `scripts/stack-release.sh` | `--sso` on activate (usage, parse, `sso_for_target`), `sso_checks` and `slot_problem` (item 2), `sso_base` (rollback flags, item 3), `config_checks` (item 3 and N4), the `sso:` plan line and step lines. |
|
||||||
| `packages/cli/tests/stack-release.test.mjs` | Fixture copies gain row 192's `--sso` marker, an old loader without `console`, and a C copy whose `ALGS` lack RS256. T56, 8 subtests. |
|
| `packages/cli/tests/stack-release.test.mjs` | Fixture copies gain row 192's `--sso` marker, an old loader without `console`, and a C copy whose `ALGS` lack RS256. T56, 10 subtests (2 in round 2). |
|
||||||
| `docs/TOOLS.md` | Usage line and two paragraphs after row 189's `--no-send`. |
|
| `docs/TOOLS.md` | Usage line and two paragraphs after row 189's `--no-send`. |
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
@@ -70,11 +70,14 @@ For the row 200 window plan, word for word:
|
|||||||
`scripts/stack-release.sh rollback acme --plan` must print no `refuses config-console-key:` line. If it
|
`scripts/stack-release.sh rollback acme --plan` must print no `refuses config-console-key:` line. If it
|
||||||
prints one, restore `config.json` from the window's step 1 backup and run the plan again. The rollback itself
|
prints one, restore `config.json` from the window's step 1 backup and run the plan again. The rollback itself
|
||||||
refuses on the same check, so a skipped step stops it before anything stops.
|
refuses on the same check, so a skipped step stops it before anything stops.
|
||||||
|
- **Back-out line (webby-0028 N4, sage-1432).** Once a copy with `--sso` support is live, no verb turns `--sso`
|
||||||
|
off. Only a rollback to a copy before row 192 gets the preview set back, after the `console` key is removed
|
||||||
|
(the check line above).
|
||||||
|
|
||||||
## Tests run (this worktree, final tree)
|
## Tests run (this worktree, final tree)
|
||||||
|
|
||||||
- `node --test --test-name-pattern='^T56' tests/stack-release.test.mjs`: 8/8
|
- `node --test --test-name-pattern='^T56' tests/stack-release.test.mjs`: 10/10 on the round 2 tree
|
||||||
(`/mnt/storage/scratch/launchpad-r200/t56-6.log`).
|
(`/mnt/storage/scratch/launchpad-r200/t56-r2c.log`). Round 1 was 8/8 (`t56-6.log`).
|
||||||
- The whole file, the cli suite and every `scripts/test-*.sh` go to Sage's gate. T1 to T55 have not run on
|
- The whole file, the cli suite and every `scripts/test-*.sh` go to Sage's gate. T1 to T55 have not run on
|
||||||
this tree.
|
this tree.
|
||||||
|
|
||||||
@@ -91,11 +94,16 @@ For the row 200 window plan, word for word:
|
|||||||
| 7 | keep `--public-host` under `--sso` (item 1) | KILLED |
|
| 7 | keep `--public-host` under `--sso` (item 1) | KILLED |
|
||||||
| 8 | rollback takes the latest activation onto the copy, not the arrival (sage-1389) | KILLED |
|
| 8 | rollback takes the latest activation onto the copy, not the arrival (sage-1389) | KILLED |
|
||||||
| 9 | `sso-config` names no first step (N4) | KILLED |
|
| 9 | `sso-config` names no first step (N4) | KILLED |
|
||||||
|
| 10 | `slot_problem` compares the unresolved `REPO` (round 2, N1) | not run, Sage's gate |
|
||||||
|
| 11 | `proof_file_problem` compares the unresolved `REPO` (round 2, N1) | not run, Sage's gate |
|
||||||
|
| 12 | `config_checks` passes a config that doesn't parse (round 2, N2) | not run, expected equivalent |
|
||||||
|
|
||||||
9/9 killed (`/mnt/storage/scratch/launchpad-r200/mutants.log`). The script restored `stack-release.sh`, and it
|
9/9 killed (`/mnt/storage/scratch/launchpad-r200/mutants.log`). The script restored `stack-release.sh`, and it
|
||||||
matches its pre-run sha256. No mutant hit the 300 s timeout. This batch ran without Sage's slot (sage-1414),
|
matches its pre-run sha256. No mutant hit the 300 s timeout. This batch ran without Sage's slot (sage-1414),
|
||||||
while Darkwing's gate was running, so Sage reruns all 9 on the row 200 gate and doesn't take these results
|
while Darkwing's gate was running, so Sage reruns all 9 on the row 200 gate and doesn't take these results
|
||||||
(sage-1423).
|
(sage-1423). Mutants 10 to 12 are new in round 2 and have not run: they need Sage's slot (sage-1414). Mutant 5's
|
||||||
|
pattern now names the `holds` message, because the round 2 message also starts with `config-console-key "$cfg`.
|
||||||
|
Every pattern matches once on the round 2 tree.
|
||||||
|
|
||||||
## Choices
|
## Choices
|
||||||
|
|
||||||
@@ -126,6 +134,25 @@ while Darkwing's gate was running, so Sage reruns all 9 on the row 200 gate and
|
|||||||
The only way back to the preview set is a rollback to a copy before row 192, which removes the `console` key
|
The only way back to the preview set is a rollback to a copy before row 192, which removes the `console` key
|
||||||
first (the second check line above).
|
first (the second check line above).
|
||||||
|
|
||||||
|
## Round 2 (sage-1432: N1 and N2; N3 and N4 stay notes)
|
||||||
|
|
||||||
|
- **N1.** `slot_problem` (`:847`) and `proof_file_problem` (`:989`) now compare the slot's real path with
|
||||||
|
`realpath -m` of both `REPO` and `COPIES`, and refuse "cannot be resolved" if either fails. T56's
|
||||||
|
"round 2 (webby-0028 N1)" subtest runs the script through a symlink to the fixture repo
|
||||||
|
(`repo-link.200/scripts/stack-release.sh`). A proof file inside the repository is refused `proof-file-invalid`,
|
||||||
|
and a slot whose secrets directory links into the repository is refused `sso-slot` "is inside the repository
|
||||||
|
or the copies". Mutants 10 and 11 undo each check.
|
||||||
|
- **N2, with a deviation.** `config_checks` (`:867`) now tells "no key" (node exits 1) from "doesn't parse"
|
||||||
|
(exits 2) and refuses `config-console-key` "cannot be read as JSON" on the second. But webby-0028's case
|
||||||
|
doesn't reach it: `load_config` (common.sh, through `scripts/mosaic-config.mjs env`) exits 1 on a config that
|
||||||
|
doesn't parse, before anything is read or stopped. So the refusal is a guard for a config that changes after
|
||||||
|
`load_config` ran, and no test reaches it without a race. The T56 subtest "round 2 (webby-0028 N2)" checks
|
||||||
|
what does happen: activate, rollback and `activate --plan` each exit 1 with "common: configuration load
|
||||||
|
failed", the pointer, the start/stop log and the activation log are unchanged, and activate works after the
|
||||||
|
config is restored. Mutant 12 drops the guard and is expected to survive as equivalent for that reason. Sage
|
||||||
|
rules whether to keep the guard, as with row 192's mutant 23.
|
||||||
|
- **N4.** The back-out line is in the window plan section above.
|
||||||
|
|
||||||
## Not done here
|
## Not done here
|
||||||
|
|
||||||
- No change to the live pointer, units or config. The phase 2 window is Sage's and Mos's.
|
- No change to the live pointer, units or config. The phase 2 window is Sage's and Mos's.
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
6ee1adb3951aeb1e3862cb119c12b600b88641f325912c09840b841761aa12dd docs/TOOLS.md
|
6ee1adb3951aeb1e3862cb119c12b600b88641f325912c09840b841761aa12dd docs/TOOLS.md
|
||||||
0c1aa38932982b72ea71a6d2d785f6412143c298ea6f2b448c4b4ee615a66da8 packages/cli/tests/stack-release.test.mjs
|
9b231532d14b21d0d39ca89035d9067e827e18cc1cf3dfe298bef840ae87ff0c packages/cli/tests/stack-release.test.mjs
|
||||||
6cb5f90a85eb86973a5176934f867c7282509e49830933aaa998f70297c79392 scripts/stack-release.sh
|
d24e75e9927623ad106b55bce82e40ddf7677d483626fb46923c3b3c7c1148ad scripts/stack-release.sh
|
||||||
@@ -11,11 +11,14 @@ M = [
|
|||||||
('no sso-config refusal (item 2)', [(' check_refuse sso-config "$TO', ' : "$TO')]),
|
('no sso-config refusal (item 2)', [(' check_refuse sso-config "$TO', ' : "$TO')]),
|
||||||
('no sso-slot refusal (item 2)', [('[ -z "$why" ] || check_refuse sso-slot', '[ -z "$why" ] || : sso-slot')]),
|
('no sso-slot refusal (item 2)', [('[ -z "$why" ] || check_refuse sso-slot', '[ -z "$why" ] || : sso-slot')]),
|
||||||
('no sso-rollback-flags refusal (item 3)', [(' check_refuse sso-rollback-flags "$TO', ' : "$TO')]),
|
('no sso-rollback-flags refusal (item 3)', [(' check_refuse sso-rollback-flags "$TO', ' : "$TO')]),
|
||||||
('no config-console-key refusal (item 3)', [(' check_refuse config-console-key "$cfg', ' : "$cfg')]),
|
('no config-console-key refusal (item 3)', [(' check_refuse config-console-key "$cfg holds', ' : "$cfg holds')]),
|
||||||
('keep --auth-header under --sso (item 1)', [(" --public-host | --auth-header | --allow-user)", " --public-host | --allow-user)")]),
|
('keep --auth-header under --sso (item 1)', [(" --public-host | --auth-header | --allow-user)", " --public-host | --allow-user)")]),
|
||||||
('keep --public-host under --sso (item 1)', [(" --public-host | --auth-header | --allow-user)", " --auth-header | --allow-user)")]),
|
('keep --public-host under --sso (item 1)', [(" --public-host | --auth-header | --allow-user)", " --auth-header | --allow-user)")]),
|
||||||
('rollback takes the latest activation onto the copy, not the arrival (sage-1389)', [('o.to===current&&o.from!==current){process.stdout.write(`${o.activation', 'o.to===current){process.stdout.write(`${o.activation')]),
|
('rollback takes the latest activation onto the copy, not the arrival (sage-1389)', [('o.to===current&&o.from!==current){process.stdout.write(`${o.activation', 'o.to===current){process.stdout.write(`${o.activation')]),
|
||||||
('sso-config names no first step (N4)', [(' first="; activate $TO without --sso first, then write console.sso, then run activate $TO --sso"', ' first=""')]),
|
('sso-config names no first step (N4)', [(' first="; activate $TO without --sso first, then write console.sso, then run activate $TO --sso"', ' first=""')]),
|
||||||
|
('slot_problem compares the unresolved REPO (round 2, webby-0028 N1)', [(' case "$real" in "$repo" | "$repo"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac\n [ "$size" != 0 ]', ' case "$real" in "$REPO" | "$REPO"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac\n [ "$size" != 0 ]')]),
|
||||||
|
('proof_file_problem compares the unresolved REPO (round 2, webby-0028 N1)', [(' case "$real" in "$repo" | "$repo"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac\n [ "$mode" = 600 ]', ' case "$real" in "$REPO" | "$REPO"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac\n [ "$mode" = 600 ]')]),
|
||||||
|
('config_checks passes a config that does not parse (round 2, webby-0028 N2; expected equivalent)', [(' [ "$key" != 1 ] || return 0', ' [ "$key" = 0 ] || return 0')]),
|
||||||
]
|
]
|
||||||
sha = lambda b: hashlib.sha256(b).hexdigest()
|
sha = lambda b: hashlib.sha256(b).hexdigest()
|
||||||
only = sys.argv[2:]
|
only = sys.argv[2:]
|
||||||
|
|||||||
@@ -2007,6 +2007,33 @@ test('T56 row 200: activate --sso, its refusals before any stop, and the rollbac
|
|||||||
r=await prun(e,'rollback','acme');assert.equal(r.code,0,r.out+r.err);assert.equal(pointer(e).copy,B);
|
r=await prun(e,'rollback','acme');assert.equal(r.code,0,r.out+r.err);assert.equal(pointer(e).copy,B);
|
||||||
await noLeak(e);
|
await noLeak(e);
|
||||||
});
|
});
|
||||||
|
await t.test('round 2 (webby-0028 N1): run through a symlinked repository path, a slot or a proof file inside the repository still refuses',async(t)=>{
|
||||||
|
const link=join(base,'repo-link.200');if(!existsSync(link))symlinkSync(repo,link);
|
||||||
|
const e=await mk(t);e.sr=join(link,'scripts','stack-release.sh');await seedPreview(e);
|
||||||
|
const f=await proofFile(e,'proof.200',0o600,mkdtempSync(join(repo,'t56-proof-')));t.after(()=>rmSync(dirname(f),{recursive:true,force:true}));
|
||||||
|
await refused(e,['activate',C1,'acme',...pair(f)],'proof-file-invalid',`--proof-file ${f} is inside the repository or the copies`);
|
||||||
|
await ssoConfig(e,{slot:false});
|
||||||
|
let r=await prun(e,'activate',C1,'acme');assert.equal(r.code,0,r.out+r.err);
|
||||||
|
const inRepo=mkdtempSync(join(repo,'t56-slot-'));t.after(()=>rmSync(inRepo,{recursive:true,force:true}));
|
||||||
|
writeFileSync(join(inRepo,SLOT),'x\n',{mode:0o600});chmodSync(join(inRepo,SLOT),0o600);
|
||||||
|
const secrets=dirname(slotPath(e));rmSync(secrets,{recursive:true});symlinkSync(inRepo,secrets);
|
||||||
|
await refused(e,['activate',C1,'acme','--sso'],'sso-slot',`console.sso provider a client secret slot ${slotPath(e)} is inside the repository or the copies`);
|
||||||
|
await noLeak(e);
|
||||||
|
});
|
||||||
|
await t.test('round 2 (webby-0028 N2): a config.json that does not parse stops the run before anything is read or stopped',async(t)=>{
|
||||||
|
const e=await mk(t);await seedPreview(e);
|
||||||
|
// load_config exits first, before the pointer, the log or a unit is read; config_checks' own
|
||||||
|
// refusal is the guard for a file that changes after that (BUILD.md, round 2).
|
||||||
|
const good=readFileSync(e.cfg,'utf8');writeFileSync(e.cfg,good.slice(0,-1));
|
||||||
|
const before=fp(e),n=calls(e).length,logs=logLines(e).length;
|
||||||
|
for(const args of [['activate',C1,'acme'],['rollback','acme'],['activate',C1,'acme','--plan']]){
|
||||||
|
const r=await prun(e,...args);assert.equal(r.code,1,args.join(' '));assert.match(r.err,/common: configuration load failed\n$/);
|
||||||
|
}
|
||||||
|
assert.deepEqual(fp(e),before);assert.deepEqual(startsStops(e,n),[]);assert.equal(logLines(e).length,logs);
|
||||||
|
writeFileSync(e.cfg,good);
|
||||||
|
const r=await prun(e,'activate',C1,'acme');assert.equal(r.code,0,r.out+r.err);
|
||||||
|
await noLeak(e);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- rehearse (D2, row 163; the brief's "Stack rehearsal on a scratch root") ---
|
// --- rehearse (D2, row 163; the brief's "Stack rehearsal on a scratch root") ---
|
||||||
|
|||||||
@@ -845,7 +845,7 @@ sso_checks() {
|
|||||||
# Why a slot file can't serve, or nothing: readSlot's checks in packages/webui/src/sso.mjs, in its
|
# Why a slot file can't serve, or nothing: readSlot's checks in packages/webui/src/sso.mjs, in its
|
||||||
# order, from lstat, stat and realpath only, then a size of 0 for its one-non-empty-line rule.
|
# order, from lstat, stat and realpath only, then a size of 0 for its one-non-empty-line rule.
|
||||||
slot_problem() {
|
slot_problem() {
|
||||||
local st uid mode size type real copies
|
local st uid mode size type real copies repo
|
||||||
[ -e "$1" ] || [ -L "$1" ] || { echo "is missing"; return; }
|
[ -e "$1" ] || [ -L "$1" ] || { echo "is missing"; return; }
|
||||||
[ ! -L "$1" ] || { echo "is a symbolic link"; return; }
|
[ ! -L "$1" ] || { echo "is a symbolic link"; return; }
|
||||||
st="$(stat -c '%u:%a:%s:%F' -- "$1" 2>/dev/null)" || { echo "cannot be read with stat"; return; }
|
st="$(stat -c '%u:%a:%s:%F' -- "$1" 2>/dev/null)" || { echo "cannot be read with stat"; return; }
|
||||||
@@ -855,8 +855,9 @@ slot_problem() {
|
|||||||
case "$type" in "regular file" | "regular empty file") ;; *) echo "is not a regular file"; return ;; esac
|
case "$type" in "regular file" | "regular empty file") ;; *) echo "is not a regular file"; return ;; esac
|
||||||
[ "$uid" = "$(id -u)" ] || { echo "is owned by uid $uid, not $(id -u), the uid this script runs as"; return; }
|
[ "$uid" = "$(id -u)" ] || { echo "is owned by uid $uid, not $(id -u), the uid this script runs as"; return; }
|
||||||
(( (8#$mode & 8#077) == 0 )) || { echo "has mode $mode, which gives group or other permission bits"; return; }
|
(( (8#$mode & 8#077) == 0 )) || { echo "has mode $mode, which gives group or other permission bits"; return; }
|
||||||
real="$(realpath -e -- "$1")" && copies="$(realpath -m -- "$COPIES")" || { echo "cannot be resolved"; return; }
|
# REPO is $(pwd), which keeps a symlink in the path the script ran by (webby-0028 N1).
|
||||||
case "$real" in "$REPO" | "$REPO"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac
|
real="$(realpath -e -- "$1")" && copies="$(realpath -m -- "$COPIES")" && repo="$(realpath -m -- "$REPO")" || { echo "cannot be resolved"; return; }
|
||||||
|
case "$real" in "$repo" | "$repo"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac
|
||||||
[ "$size" != 0 ] || echo "is empty"
|
[ "$size" != 0 ] || echo "is empty"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -865,7 +866,17 @@ slot_problem() {
|
|||||||
# target's own scripts/mosaic-config.mjs validate; its output, the resolved config, is not kept.
|
# target's own scripts/mosaic-config.mjs validate; its output, the resolved config, is not kept.
|
||||||
config_checks() {
|
config_checks() {
|
||||||
local cfg="${MOSAIC_CONFIG:-$HOME/.config/mosaic-dev/config.json}" out loader="$COPIES/$TO/scripts/mosaic-config.mjs" first
|
local cfg="${MOSAIC_CONFIG:-$HOME/.config/mosaic-dev/config.json}" out loader="$COPIES/$TO/scripts/mosaic-config.mjs" first
|
||||||
node -e 'process.exit(Object.hasOwn(JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")),"console")?0:1)' -- "$cfg" 2>/dev/null || return 0
|
# 0: a console key; 1: no key, or no file; 2: the file doesn't parse, so whether it holds the key
|
||||||
|
# is unknown, and the run refuses rather than pass it (webby-0028 N2). load_config already exits on
|
||||||
|
# such a file before anything is read or stopped, so this is reached only if the file changes after it.
|
||||||
|
local key=0
|
||||||
|
node -e 'const fs=require("fs"),f=process.argv[1];if(!fs.existsSync(f))process.exit(1);let d;try{d=JSON.parse(fs.readFileSync(f,"utf8"))}catch{process.exit(2)}
|
||||||
|
process.exit(d!==null&&typeof d==="object"&&Object.hasOwn(d,"console")?0:1)' -- "$cfg" 2>/dev/null || key=$?
|
||||||
|
[ "$key" != 1 ] || return 0
|
||||||
|
if [ "$key" != 0 ]; then
|
||||||
|
check_refuse config-console-key "$cfg cannot be read as JSON, so whether $TO's loader reads it is unknown; fix the config first, then run the $OP again"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
if [ ! -f "$loader" ]; then out="it has no scripts/mosaic-config.mjs"
|
if [ ! -f "$loader" ]; then out="it has no scripts/mosaic-config.mjs"
|
||||||
elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)"; then return 0
|
elif out="$(MOSAIC_CONFIG="$cfg" node "$loader" validate 2>&1 >/dev/null)"; then return 0
|
||||||
fi
|
fi
|
||||||
@@ -976,7 +987,7 @@ proof_checks() {
|
|||||||
# Why the proof file can't serve, or nothing. lstat, stat and realpath only; the order follows
|
# Why the proof file can't serve, or nothing. lstat, stat and realpath only; the order follows
|
||||||
# webui-service.sh render, which refuses the same files.
|
# webui-service.sh render, which refuses the same files.
|
||||||
proof_file_problem() {
|
proof_file_problem() {
|
||||||
local st uid mode type real copies
|
local st uid mode type real copies repo
|
||||||
case "$1" in /*) ;; *) echo "is not an absolute path"; return ;; esac
|
case "$1" in /*) ;; *) echo "is not an absolute path"; return ;; esac
|
||||||
case "$1" in *[!A-Za-z0-9/._+-]*) echo "holds a character outside [A-Za-z0-9/._+-]"; return ;; esac
|
case "$1" in *[!A-Za-z0-9/._+-]*) echo "holds a character outside [A-Za-z0-9/._+-]"; return ;; esac
|
||||||
[ ! -L "$1" ] || { echo "is a symlink"; return; }
|
[ ! -L "$1" ] || { echo "is a symlink"; return; }
|
||||||
@@ -984,8 +995,8 @@ proof_file_problem() {
|
|||||||
uid="${st%%:*}" st="${st#*:}"
|
uid="${st%%:*}" st="${st#*:}"
|
||||||
mode="${st%%:*}" type="${st#*:}"
|
mode="${st%%:*}" type="${st#*:}"
|
||||||
case "$type" in "regular file" | "regular empty file") ;; *) echo "is not a regular file"; return ;; esac
|
case "$type" in "regular file" | "regular empty file") ;; *) echo "is not a regular file"; return ;; esac
|
||||||
real="$(realpath -e -- "$1")" && copies="$(realpath -m -- "$COPIES")" || { echo "cannot be resolved"; return; }
|
real="$(realpath -e -- "$1")" && copies="$(realpath -m -- "$COPIES")" && repo="$(realpath -m -- "$REPO")" || { echo "cannot be resolved"; return; }
|
||||||
case "$real" in "$REPO" | "$REPO"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac
|
case "$real" in "$repo" | "$repo"/* | "$copies" | "$copies"/*) echo "is inside the repository or the copies"; return ;; esac
|
||||||
[ "$mode" = 600 ] || [ "$mode" = 400 ] || { echo "has mode $mode, not 600 or 400"; return; }
|
[ "$mode" = 600 ] || [ "$mode" = 400 ] || { echo "has mode $mode, not 600 or 400"; return; }
|
||||||
[ "$uid" = "$(id -u)" ] || echo "is owned by uid $uid, not $(id -u), the uid this script runs as"
|
[ "$uid" = "$(id -u)" ] || echo "is owned by uid $uid, not $(id -u), the uid this script runs as"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user