fix(commons,daemon,skills): Windows-compat handler import guard (green CI)
The cross-branch import guard's same-branch check used a POSIX-only path test,
so on Windows (backslash paths) it failed to recognize a branch importing its
OWN handlers -> ImportError at collection, failing all commons + 2 daemon tests
on the Windows CI runner. (Unmasked once the pathspec fix let collection proceed.)
- commons: '/commons/' substring -> 'commons' in Path(caller_file).parts
- daemon + skills: add .replace('\\','/') before the check (matches the idiom
already used by 15 other branches' guards)
- Convert AIPASS_DEBUG_GUARD debug print() -> sys.stderr.write (cli standard;
avoids import-time logger dependency inside the guard)
Security semantics unchanged: same-branch allowed, cross-branch still blocked
(verified cross-platform). commons+daemon audit 100%, 700 daemon+skills tests
pass, commons 449 tests pass. (skills local 99% = untracked skills_json orphans,
not in CI.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
95a2d1a254
commit
6aabc8bfe6
@@ -55,8 +55,8 @@ def _guard_branch_access():
|
||||
if os.environ.get("AIPASS_DEBUG_GUARD"):
|
||||
import sys
|
||||
|
||||
print(f"[GUARD DEBUG] caller_file = {caller_file}", file=sys.stderr)
|
||||
print(f"[GUARD DEBUG] import_line = {import_line}", file=sys.stderr)
|
||||
sys.stderr.write(f"[GUARD DEBUG] caller_file = {caller_file}\n")
|
||||
sys.stderr.write(f"[GUARD DEBUG] import_line = {import_line}\n")
|
||||
|
||||
if caller_file is None:
|
||||
stack = inspect.stack()
|
||||
@@ -65,9 +65,10 @@ def _guard_branch_access():
|
||||
return # Allow command-line Python through
|
||||
return
|
||||
|
||||
# IMPORTANT: Commons is at src/commons/, not src/aipass/commons/
|
||||
# Check if caller is from within the commons directory
|
||||
if "/commons/" in caller_file:
|
||||
# Check if caller is from within the commons directory.
|
||||
# Use path PARTS (not a "/commons/" substring) so the same-branch check
|
||||
# works on Windows too, where paths use backslash separators.
|
||||
if "commons" in Path(caller_file).parts:
|
||||
return # Same branch, allowed
|
||||
|
||||
caller_branch = _extract_branch_name(caller_file)
|
||||
|
||||
@@ -27,6 +27,7 @@ from aipass.commons.apps.handlers.json import json_handler
|
||||
# SEAL A TIME CAPSULE
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def seal_capsule(args: List[str]) -> dict:
|
||||
"""
|
||||
Seal a time capsule that opens after N days.
|
||||
@@ -51,6 +52,7 @@ def seal_capsule(args: List[str]) -> dict:
|
||||
days = max(1, min(365, days))
|
||||
|
||||
from aipass.commons.apps.modules.commons_identity import get_caller_branch
|
||||
|
||||
caller = get_caller_branch()
|
||||
if not caller:
|
||||
return {"success": False, "error": "Could not detect calling branch. Run from a branch directory."}
|
||||
@@ -64,8 +66,7 @@ def seal_capsule(args: List[str]) -> dict:
|
||||
conn = get_db()
|
||||
|
||||
cursor = conn.execute(
|
||||
"INSERT INTO time_capsules (creator, title, content, opens_at) "
|
||||
"VALUES (?, ?, ?, ?)",
|
||||
"INSERT INTO time_capsules (creator, title, content, opens_at) VALUES (?, ?, ?, ?)",
|
||||
(creator, title, content, opens_at),
|
||||
)
|
||||
capsule_id = cursor.lastrowid
|
||||
@@ -91,6 +92,7 @@ def seal_capsule(args: List[str]) -> dict:
|
||||
# LIST TIME CAPSULES
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def list_capsules(args: List[str]) -> dict:
|
||||
"""
|
||||
List all time capsules with status info.
|
||||
@@ -103,9 +105,7 @@ def list_capsules(args: List[str]) -> dict:
|
||||
try:
|
||||
conn = get_db()
|
||||
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM time_capsules ORDER BY opens_at ASC"
|
||||
).fetchall()
|
||||
rows = conn.execute("SELECT * FROM time_capsules ORDER BY opens_at ASC").fetchall()
|
||||
|
||||
close_db(conn)
|
||||
|
||||
@@ -145,6 +145,7 @@ def list_capsules(args: List[str]) -> dict:
|
||||
# OPEN A TIME CAPSULE
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def open_capsule(args: List[str]) -> dict:
|
||||
"""
|
||||
Open a time capsule if its opens_at date has passed.
|
||||
@@ -164,6 +165,7 @@ def open_capsule(args: List[str]) -> dict:
|
||||
return {"success": False, "error": "Capsule ID must be a number"}
|
||||
|
||||
from aipass.commons.apps.modules.commons_identity import get_caller_branch
|
||||
|
||||
caller = get_caller_branch()
|
||||
if not caller:
|
||||
return {"success": False, "error": "Could not detect calling branch. Run from a branch directory."}
|
||||
@@ -173,9 +175,7 @@ def open_capsule(args: List[str]) -> dict:
|
||||
try:
|
||||
conn = get_db()
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT * FROM time_capsules WHERE id = ?", (capsule_id,)
|
||||
).fetchone()
|
||||
row = conn.execute("SELECT * FROM time_capsules WHERE id = ?", (capsule_id,)).fetchone()
|
||||
|
||||
if not row:
|
||||
close_db(conn)
|
||||
|
||||
@@ -66,8 +66,8 @@ def _guard_branch_access():
|
||||
if os.environ.get("AIPASS_DEBUG_GUARD"):
|
||||
import sys
|
||||
|
||||
print(f"[GUARD DEBUG] caller_file = {caller_file}", file=sys.stderr)
|
||||
print(f"[GUARD DEBUG] import_line = {import_line}", file=sys.stderr)
|
||||
sys.stderr.write(f"[GUARD DEBUG] caller_file = {caller_file}\n")
|
||||
sys.stderr.write(f"[GUARD DEBUG] import_line = {import_line}\n")
|
||||
|
||||
if caller_file is None:
|
||||
# Can't determine caller from real files
|
||||
@@ -82,8 +82,8 @@ def _guard_branch_access():
|
||||
# Check if caller is from our branch
|
||||
# MY_BRANCH is "aipass.daemon" (dotted), but filesystem uses "/aipass/daemon/"
|
||||
branch_path = "/" + MY_BRANCH.replace(".", "/") + "/"
|
||||
if branch_path in caller_file:
|
||||
return # Same branch, allowed
|
||||
if branch_path in caller_file.replace("\\", "/"):
|
||||
return # Same branch, allowed (normalize Windows backslash paths)
|
||||
|
||||
# External caller - block access
|
||||
caller_branch = _extract_branch_name(caller_file)
|
||||
|
||||
@@ -66,8 +66,8 @@ def _guard_branch_access():
|
||||
if os.environ.get("AIPASS_DEBUG_GUARD"):
|
||||
import sys
|
||||
|
||||
print(f"[GUARD DEBUG] caller_file = {caller_file}", file=sys.stderr)
|
||||
print(f"[GUARD DEBUG] import_line = {import_line}", file=sys.stderr)
|
||||
sys.stderr.write(f"[GUARD DEBUG] caller_file = {caller_file}\n")
|
||||
sys.stderr.write(f"[GUARD DEBUG] import_line = {import_line}\n")
|
||||
|
||||
if caller_file is None:
|
||||
stack = inspect.stack()
|
||||
@@ -94,7 +94,7 @@ def _guard_branch_access():
|
||||
return
|
||||
|
||||
# Check if caller is from our branch
|
||||
if f"/{MY_BRANCH}/" in caller_file:
|
||||
if f"/{MY_BRANCH}/" in caller_file.replace("\\", "/"):
|
||||
return
|
||||
|
||||
# External caller - block access
|
||||
|
||||
Reference in New Issue
Block a user