Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion core/domain/execution_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@


class ExecutionAccessPreset(StrEnum):
"""User-facing access choices shared by every DeepCode client."""
"""User-facing access choices shared by every DeepCode client.

借鉴 Claude Code ``--allow-dangerously-skip-permissions`` (2026-08-19):
危险操作必须"显式命名危险" —— 跳过全部权限校验的逃生通道叫
``dangerous_skip`` 而不是沉默的 full_access, 让使用者与审计日志
都能一眼看到这是危险选择。
"""

ASK = "ask"
READ_ONLY = "read_only"
FULL_ACCESS = "full_access"
DANGEROUS_SKIP = "dangerous_skip"


class FilesystemScope(StrEnum):
Expand Down Expand Up @@ -178,6 +185,15 @@ def _pattern_specificity(pattern: str) -> int:
FilesystemScope.UNRESTRICTED,
ApprovalPolicy.NEVER,
),
# 危险逃生通道: 显式命名 (借鉴 Claude Code --allow-dangerously-skip-permissions)。
# 与 FULL_ACCESS 同强度, 但名字自带"危险"警示, 供日志/UI 明确区分;
# 选择它等于明确声明"我知道这很危险, 仍要跳过全部权限校验"。
ExecutionAccessPreset.DANGEROUS_SKIP: (
ExecutionPermissionMode.FULL_AUTO,
False,
FilesystemScope.UNRESTRICTED,
ApprovalPolicy.NEVER,
),
}


Expand Down
63 changes: 62 additions & 1 deletion core/harness/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

from __future__ import annotations

import os
import re
from pathlib import Path
from typing import Any

Expand All @@ -41,6 +43,65 @@
_REMINDER_OPEN = "<system-reminder>"
_REMINDER_CLOSE = "</system-reminder>"
_REMINDER_CLOSE_ESCAPED = "&lt;/system-reminder&gt;"
# 借鉴 Claude Code 的 CLAUDE.md 排除模式 (ignore 配置): 逗号分隔的 glob 模式
# (如 "**/code/CLAUDE.md,**/vendor/**")。命中的指令文件跳过不注入 —— 避免
# monorepo 子目录/第三方代码的指令污染主提示词。
_INSTRUCTION_EXCLUDE_ENV = "DEEPCODE_INSTRUCTION_EXCLUDES"
_EXCLUDE_RE_CACHE: dict[str, Any] = {} # pattern -> compiled regex


def _glob_to_re(pattern: str):
"""glob → regex: ** 匹配任意层级 (含零层), * / ? 不跨路径分隔符。"""
compiled = _EXCLUDE_RE_CACHE.get(pattern)
if compiled is not None:
return compiled
parts = []
i, n = 0, len(pattern)
while i < n:
c = pattern[i]
if c == "*":
if i + 1 < n and pattern[i + 1] == "*":
if i + 2 < n and pattern[i + 2] in "/\\":
# **/ → (?:.*/)? : 任意层级前缀(可选)。不能用 .* —— 那会让
# "**/vendor/**" 误匹配 "vendorized/..." 这类前缀同名的路径。
parts.append(r"(?:.*/)?")
i += 3
else:
# 尾部 ** → 任意剩余(含层级)
parts.append(".*")
i += 2
else:
parts.append(r"[^/\\]*")
i += 1
elif c == "?":
parts.append(r"[^/\\]")
i += 1
else:
parts.append(re.escape(c))
i += 1
compiled = re.compile("^" + "".join(parts) + "$", re.IGNORECASE)
_EXCLUDE_RE_CACHE[pattern] = compiled
return compiled


def _instruction_excluded(candidate: Path) -> bool:
"""Whether the candidate instruction file is excluded by pattern.

逗号分隔 glob, 如 ``**/code/CLAUDE.md,**/vendor/**``; 用正斜杠规范化
路径后匹配, 兼容 Windows 反斜杠路径。非法模式被忽略, 不阻断加载。
"""
patterns = [p.strip() for p in
os.environ.get(_INSTRUCTION_EXCLUDE_ENV, "").split(",") if p.strip()]
if not patterns:
return False
cand = str(candidate).replace("\\", "/")
for pat in patterns:
try:
if _glob_to_re(pat.replace("\\", "/")).match(cand):
return True
except (re.error, ValueError):
continue # 非法 glob 模式忽略, 不阻断加载
return False


def memory_dir(workspace: str | Path) -> Path:
Expand Down Expand Up @@ -135,7 +196,7 @@ def project_instructions(workspace: str | Path) -> str:
for directory in search_dirs:
for name in _PROJECT_FILES:
candidate = directory / name
if candidate.is_file():
if candidate.is_file() and not _instruction_excluded(candidate):
try:
body = candidate.read_text(
encoding="utf-8", errors="replace"
Expand Down
34 changes: 34 additions & 0 deletions tests/test_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
sys.path.insert(0, str(ROOT))

from core.harness.memory import ( # noqa: E402
_INSTRUCTION_EXCLUDE_ENV,
_MAX_INJECT_CHARS,
MemoryTool,
_instruction_excluded,
memory_dir,
project_instructions,
system_preamble,
Expand All @@ -36,6 +38,38 @@ def test_project_instructions_prefers_agents_md(tmp_path):
assert "spaces" not in out # AGENTS.md wins over CLAUDE.md


def test_instruction_excluded_matches_globs(monkeypatch):
monkeypatch.setenv(
_INSTRUCTION_EXCLUDE_ENV, "**/code/CLAUDE.md,**/vendor/**"
)
# 匹配: 任意层级前缀 + 目录段精确匹配
assert _instruction_excluded(Path("repo/code/CLAUDE.md"))
assert _instruction_excluded(Path("repo/vendor/x/AGENTS.md"))
assert _instruction_excluded(Path("repo/vendor/AGENTS.md"))
assert _instruction_excluded(Path("code/CLAUDE.md")) # 零层前缀
# 反例: 前缀同名目录不误匹配 (vendorized ≠ vendor/)
assert not _instruction_excluded(Path("repo/CLAUDE.md"))
assert not _instruction_excluded(Path("repo/vendorized/AGENTS.md"))
assert not _instruction_excluded(Path("repo/vendorized/x/CLAUDE.md"))


def test_project_instructions_skips_excluded_file(tmp_path, monkeypatch):
repo = tmp_path / "repo"
(repo / ".git").mkdir(parents=True)
(repo / "code").mkdir()
(repo / "CLAUDE.md").write_text("root instructions")
(repo / "code" / "CLAUDE.md").write_text("subdir instructions")
monkeypatch.setenv(_INSTRUCTION_EXCLUDE_ENV, "**/code/CLAUDE.md")
out = project_instructions(repo / "code")
assert "root instructions" in out
assert "subdir instructions" not in out


def test_instruction_excluded_ignores_invalid_patterns(monkeypatch):
monkeypatch.setenv(_INSTRUCTION_EXCLUDE_ENV, "**/[code/CLAUDE.md") # 非法 glob
assert not _instruction_excluded(Path("code/CLAUDE.md"))


def test_project_instructions_absent(tmp_path):
assert project_instructions(tmp_path) == ""

Expand Down
Loading