Folds Filbert's N1 per Sage's comment 28355: the envelope test sends a role cap with a tester proof. The narrowing mutant at server.mjs:50 survives the full bus suite on round 1 and is killed on round 2. Full gate passed with the real bwrap tester case running. Co-Authored-By: Claude Opus 5.5 <[email protected]>
47 lines
2.3 KiB
Python
47 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
||
# Row 110 round 2 (#1604), Sage's ask in comment 28355: the narrowing mutant.
|
||
# server.mjs:50 refuses an envelope that carries more than one of cap, human and
|
||
# tester. The mutant narrows the conflict check so a cap counts only when it is
|
||
# alone or is a reader cap (role null, not human). A role cap sent with a tester
|
||
# proof then passes the envelope and the request runs as the tester.
|
||
# Runs the FULL bus suite (node --test tests/*.test.mjs in packages/bus), not
|
||
# one named test. Restores server.mjs from the .orig copy whatever happens.
|
||
import os, shutil, subprocess, sys, signal
|
||
|
||
WT = os.path.expanduser('~/darkwing-scratch/r110/wt')
|
||
SRC = os.path.join(WT, 'packages/bus/src/server.mjs')
|
||
LOG = sys.argv[1]
|
||
OLD = " [r.cap, r.human, r.tester].filter(Boolean).length !== 1\n"
|
||
NEW = (" [r.cap && (!(r.human || r.tester) || ((c) => { try { const i = broker.identity(c); "
|
||
"return i.role === null && !i.human; } catch { return true; } })(r.cap)), r.human, r.tester]"
|
||
".filter(Boolean).length !== 1\n")
|
||
|
||
text = open(SRC).read()
|
||
if text.count(OLD) != 1:
|
||
sys.exit('STOP the line at server.mjs:50 is not unique or has moved')
|
||
if os.path.exists(SRC + '.orig'):
|
||
sys.exit('STOP server.mjs.orig already exists')
|
||
shutil.copy2(SRC, SRC + '.orig')
|
||
try:
|
||
open(SRC, 'w').write(text.replace(OLD, NEW))
|
||
env = dict(os.environ, TMPDIR=os.path.expanduser('~/darkwing-scratch/r110/tmp'))
|
||
with open(LOG, 'w') as out:
|
||
p = subprocess.Popen('node --test tests/*.test.mjs', shell=True, cwd=os.path.join(WT, 'packages/bus'),
|
||
stdout=out, stderr=subprocess.STDOUT, env=env, start_new_session=True)
|
||
try:
|
||
rc = p.wait(timeout=600)
|
||
except subprocess.TimeoutExpired:
|
||
os.killpg(p.pid, signal.SIGKILL)
|
||
p.wait()
|
||
rc = 'timeout'
|
||
finally:
|
||
shutil.move(SRC + '.orig', SRC)
|
||
restored = open(SRC).read() == text
|
||
log = open(LOG).read()
|
||
counts = [l for l in log.splitlines() if l.startswith(('ℹ pass', 'ℹ fail', 'ℹ skipped'))]
|
||
failed = [l.strip() for l in log.splitlines() if l.lstrip().startswith('✖') and 'failing tests' not in l]
|
||
print(f'mutant n1 rc={rc} {" ".join(counts)} restored={restored}')
|
||
for f in sorted(set(failed)):
|
||
print(' ' + f)
|
||
print('KILLED' if rc != 0 else 'SURVIVED')
|