Why: retain the repeatable membership check and current integration checkpoint. Evidence: SELF receipts in the task card; readiness fails on the reported data state. Impact: diagnosis metadata only; product implementation remains pending stage review. Refs: .task/feature-project-member-access.md
98 lines
4.4 KiB
Python
98 lines
4.4 KiB
Python
"""Read-only, metadata-only replay of the reported 8788 membership defect."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import sqlite3
|
|
import subprocess
|
|
|
|
|
|
def revision(path):
|
|
return subprocess.check_output(
|
|
["git", "-C", str(path), "rev-parse", "HEAD"], text=True
|
|
).strip()
|
|
|
|
|
|
def ref_revision(path, ref):
|
|
return subprocess.check_output(
|
|
["git", "-C", str(path), "rev-parse", ref], text=True
|
|
).strip()
|
|
|
|
|
|
def readonly(path):
|
|
db = sqlite3.connect(path.resolve().as_uri() + "?mode=ro", uri=True)
|
|
db.execute("PRAGMA query_only=ON")
|
|
return db
|
|
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--dev", type=Path, required=True)
|
|
parser.add_argument("--rdm", type=Path, required=True)
|
|
parser.add_argument("--assert-ready", action="store_true")
|
|
args = parser.parse_args()
|
|
runtime = args.dev / "runtime/dev"
|
|
release = (runtime / "current").resolve()
|
|
binary = release / "bin/agent-system"
|
|
binary_hash = hashlib.sha256(binary.read_bytes()).hexdigest()
|
|
print("source_objects", json.dumps({
|
|
"dev": revision(args.dev),
|
|
"backend": revision(args.dev / "modules/backend"),
|
|
"frontend": revision(args.dev / "modules/frontend"),
|
|
"rdm_source_checkout": revision(args.rdm),
|
|
"rdm_fetched_origin_main": ref_revision(args.rdm, "origin/main"),
|
|
"runtime_release": json.loads((release / "release.json").read_text())["source"],
|
|
"runtime_binary_sha256": binary_hash,
|
|
}, sort_keys=True))
|
|
a = readonly(runtime / "data/agent-system.db")
|
|
b = readonly(runtime / "data/rdm.db")
|
|
projects = a.execute("SELECT id FROM projects WHERE owner=?", ("weichen",)).fetchall()
|
|
observations = []
|
|
for (host_id,) in projects:
|
|
host_member = b.execute(
|
|
"SELECT 1 FROM rdm_members WHERE project_id=? AND owner=?", (host_id, "test")
|
|
).fetchone() is not None
|
|
binding = b.execute(
|
|
"SELECT rdm_project,state FROM rdm_host_project_map WHERE host_project=?", (host_id,)
|
|
).fetchone()
|
|
direct_project = b.execute("SELECT 1 FROM rdm_projects WHERE id=?", (host_id,)).fetchone()
|
|
resolved = binding[0] if binding and binding[1] == "ready" else host_id if direct_project else None
|
|
allowed = bool(resolved and b.execute(
|
|
"SELECT 1 FROM rdm_members WHERE project_id=? AND owner=?", (resolved, "test")
|
|
).fetchone())
|
|
if not host_member and not allowed:
|
|
continue
|
|
registered = bool(resolved and b.execute(
|
|
"SELECT 1 FROM rdm_projects WHERE id=?", (resolved,)
|
|
).fetchone())
|
|
observations.append({"target_member_row_present": host_member or allowed,
|
|
"member_row_uses_host_id": host_member,
|
|
"host_binding_ready": bool(binding and binding[1] == "ready"),
|
|
"direct_rdm_project_exists": bool(direct_project),
|
|
"resolved_rdm_project_exists": registered,
|
|
"member_authorization_from_current_source": allowed})
|
|
print("incident_metadata", json.dumps(observations, sort_keys=True))
|
|
print("member_command_audit", json.dumps(a.execute(
|
|
"SELECT command_id,state,time FROM command_audit WHERE owner=? "
|
|
"AND command_id='rdm.project.member.add' ORDER BY id DESC LIMIT 2", ("weichen",)
|
|
).fetchall()))
|
|
print("creator_is_rdm_admin", bool(b.execute(
|
|
"SELECT 1 FROM rdm_admins WHERE owner=?", ("weichen",)
|
|
).fetchone()))
|
|
print("host_name_unique_indexes", json.dumps([
|
|
(row[1], [col[2] for col in a.execute('PRAGMA index_info("' + row[1].replace('"', '""') + '")')])
|
|
for row in a.execute("PRAGMA index_list(projects)") if row[2]
|
|
]))
|
|
a.close()
|
|
b.close()
|
|
if release != (runtime / "current").resolve() or binary_hash != hashlib.sha256(binary.read_bytes()).hexdigest():
|
|
raise SystemExit("UNSTABLE_RUNTIME: repeat against a fixed release")
|
|
print("privacy_scope: no credentials, session tokens, chat content, documents or original uploads read")
|
|
print("http_browser_replay: NOT_RUN; this probe verifies stored incident metadata and source authorization rules")
|
|
if args.assert_ready:
|
|
if not observations:
|
|
raise SystemExit("NO_TARGET_MEMBER_ROWS: incident no longer matches the recorded setup")
|
|
if not all(row["resolved_rdm_project_exists"] and
|
|
row["member_authorization_from_current_source"] for row in observations):
|
|
raise SystemExit("FAIL membership_resolves_to_registered_project: stored membership cannot authorize the host project")
|
|
print("PASS membership_resolves_to_registered_project")
|