FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(codex): restore apply_patch owner guard by qiankunli · Pull Request #97 · compforge/devloop · GitHub

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (4) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
12 changes: 12 additions & 0 deletions devloop/hooks/lib/core/context.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pathlib import Path

from lib import config, repo_layout
from lib.core.domain import FileChange, Target


class PolicyContext:
Expand All @@ -32,6 +33,17 @@ def __init__(self, cwd: str, anchor_path: str = "", session_id: str = ""):
def cwd(self) -> str:
return self._cwd

def for_target(self, target: Target) -> PolicyContext:
"""Return the policy view anchored to the target being evaluated.

A single ``apply_patch`` can contain files below an aggregate workspace or even span
repositories. Repo-scoped rules must therefore resolve ownership from each file target,
not from the session cwd or one call-level anchor.
"""
if isinstance(target, FileChange):
return PolicyContext(self._cwd, anchor_path=target.path, session_id=self.session_id)
return self

@property
def anchor_abspath(self) -> str:
"""被编辑文件的绝对路径(edit 族规则做 gitignore / 路径判断用)。"""
Expand Down
7 changes: 4 additions & 3 deletions devloop/hooks/lib/core/engine.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,19 @@ def evaluate(change: Change, ctx: PolicyContext, rules: list[Rule]) -> Decision:
findings: list[Finding] = []

for target in change.targets:
target_ctx = ctx.for_target(target) if hasattr(ctx, "for_target") else ctx
kind = getattr(target, "kind", None)
applicable = [r for r in rules if r.target_kind == kind and _safe_applies(r, target, ctx)]
applicable = [r for r in rules if r.target_kind == kind and _safe_applies(r, target, target_ctx)]
if not applicable:
continue
# content-aware 规则命中 → 惰性解析(读盘+套 edit 得"改后全文"再解析 imports/decls)
if isinstance(target, FileChange) and any(r.needs_content for r in applicable):
try:
enrich(target, ctx)
enrich(target, target_ctx)
except Exception:
pass # 解析失败 → 不产 content findings(fail-open)
for r in applicable:
findings.extend(_safe_check(r, target, ctx))
findings.extend(_safe_check(r, target, target_ctx))

# mutation 级规则:不看具体 target,直接吃 Change
for r in rules:
Expand Down
11 changes: 4 additions & 7 deletions devloop/hooks/pretool_policy_edit.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""PreToolUse (Edit/Write/MultiEdit/NotebookEdit): 编辑侧策略引擎入口。
"""PreToolUse (Edit/Write/MultiEdit/NotebookEdit/apply_patch): 编辑侧策略引擎入口。

把这次文件改动投影成 `FileChange`,跑 FILE_CHANGE 规则(checkout 占有、分支失活、
requirements.txt、层级依赖 lint),deny 则在落盘前拦下。
Expand All @@ -16,14 +16,11 @@
from lib.core import engine # noqa: E402
from lib.core.context import PolicyContext # noqa: E402

_FILE_TOOLS = ("Edit", "Write", "MultiEdit", "NotebookEdit")


def decide(inp: hook_io.HookInput) -> str | None:
if not inp.is_tool(*_FILE_TOOLS):
return None
change = engine.project(inp)
ctx = PolicyContext(inp.cwd, anchor_path=inp.file_path, session_id=inp.session_id)
if not change.targets:
return None
ctx = PolicyContext(inp.cwd, session_id=inp.session_id)
decision = engine.evaluate(change, ctx, rules.REGISTRY)
if not decision.blocked:
return None
Expand Down
28 changes: 28 additions & 0 deletions devloop/tests/test_guards.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,34 @@ def test_edit_owner_guard():
outside = _hook_input("Edit", {"session_id": "sess-B", "cwd": R, "tool_input": {"file_path": f"{R}/x.py"}})
assert guard.decide(outside) is None

def test_apply_patch_owner_guard_uses_target_path():
"""Codex ``apply_patch`` must enter the edit policy and anchor owner lookup to the patched
file. Its session cwd commonly remains at the aggregate workspace root, which is not a repo.
"""
guard = _load_hook("pretool_policy_edit")
from lib.context import session as session_lock
R = "/tmp/dlut_patch_owner"
repo = f"{R}/repo"
shutil.rmtree(R, ignore_errors=True); os.makedirs(repo, exist_ok=True)
_git(repo, "init", "-q")
fp = f"{repo}/a.py"
Path(fp).write_text("old\n")
patch = f"*** Begin Patch\n*** Update File: {fp}\n@@\n-old\n+new\n*** End Patch\n"

# The hook's freeform-tool normalization stores the patch under ``input``.
inp_a = _hook_input("apply_patch", {
"session_id": "sess-A", "cwd": R, "tool_input": {"input": patch},
})
assert guard.decide(inp_a) is None
assert session_lock.read(repo)["session_id"] == "sess-A"

session_lock.acquire(repo, "sess-A", "feat/x", pid=os.getpid())
inp_b = _hook_input("apply_patch", {
"session_id": "sess-B", "cwd": R, "tool_input": {"input": patch},
})
reason = guard.decide(inp_b)
assert reason and "worktree" in reason and "owner.lock" in reason

def test_branch_merged_guard_uses_file_path():
"""INACTIVE 分支编辑拦截按 file_path 解析 repo——session cwd 在 workspace 根时
cwd-based 查找为 None,guard 此前静默失效。Also exercises the gate's SHA validation: the
Expand Down

Back | FazBrowse Home | New Git URL