claude-dev-env 7.0.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/pull-request/scripts/pull_request.py +2 -0
- package/.agents/skills/pull-request/scripts/test_pull_request.py +34 -0
- package/.agents/skills/session-continuity/AGENTS.md +1 -0
- package/.agents/skills/session-continuity/CLAUDE.md +1 -0
- package/.agents/skills/session-continuity/README.md +213 -0
- package/.agents/skills/session-continuity/SKILL.md +125 -0
- package/.agents/skills/session-continuity/continuity.mjs +447 -0
- package/.agents/skills/session-continuity/continuity.test.mjs +373 -0
- package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
- package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
- package/bin/cde.mjs +36 -8
- package/bin/cde.test.mjs +48 -0
- package/bin/ever-shipped-skills.mjs +2 -20
- package/bin/ever-shipped-skills.test.mjs +8 -0
- package/bin/expand_home_directory_tokens.mjs +4 -5
- package/bin/expand_home_directory_tokens.test.mjs +128 -0
- package/bin/git_hooks_installer.mjs +22 -3
- package/bin/git_hooks_installer.test.mjs +159 -5
- package/bin/install-session-continuity.mjs +88 -0
- package/bin/install-session-continuity.test.mjs +55 -0
- package/bin/install.codex-rules.test.mjs +5 -2
- package/bin/install.cursor-rules.test.mjs +5 -2
- package/bin/install.mjs +478 -63
- package/bin/install.policy-lint.test.mjs +56 -4
- package/bin/install.profiles.test.mjs +10 -0
- package/bin/install.prune.test.mjs +227 -17
- package/bin/install.test.mjs +500 -120
- package/bin/install.transaction.test.mjs +3 -3
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
- package/hooks/blocking/precommit_code_rules_gate.py +5 -8
- package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
- package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -244
- package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
- package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
- package/hooks/git-hooks/post_commit.py +33 -0
- package/hooks/git-hooks/pre_commit.py +18 -90
- package/hooks/git-hooks/pre_push.py +45 -541
- package/hooks/git-hooks/test_installed_policy_lint.py +55 -6
- package/hooks/git-hooks/test_native_hook_support.py +129 -0
- package/hooks/git-hooks/test_native_policy_commit.py +39 -6
- package/hooks/git-hooks/test_post_commit.py +43 -0
- package/hooks/git-hooks/test_pre_commit.py +36 -392
- package/hooks/git-hooks/test_pre_push.py +48 -1631
- package/hooks/git-hooks/test_verification_notice.py +60 -0
- package/hooks/git-hooks/test_verification_start.py +173 -0
- package/hooks/git-hooks/verification_notice.py +192 -0
- package/hooks/git-hooks/verification_notice_context.py +253 -0
- package/hooks/git-hooks/verification_notice_state.py +237 -0
- package/hooks/git-hooks/verification_start.py +100 -0
- package/hooks/hooks.json +0 -119
- package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -50
- package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +42 -185
- package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
- package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
- package/hooks/validators/run_all_validators.py +5 -1
- package/hooks/validators/security_checks.py +57 -2
- package/hooks/validators/test_hook_timing_harness.py +2 -5
- package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
- package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
- package/hooks/validators/test_security_checks.py +57 -0
- package/package.json +1 -1
- package/rules/confirm-implementation-forks.md +2 -0
- package/rules/git-workflow.md +12 -0
- package/rules/pstack-host-mapping.md +48 -0
- package/rules/vault-context.md +3 -5
- package/rules/verify-runtime-state.md +7 -0
- package/scripts/audit-hooks-cli.mjs +5 -0
- package/scripts/audit-hooks.mjs +72 -16
- package/scripts/automatic_advisory/__init__.py +1 -0
- package/scripts/automatic_advisory/checkout.py +51 -0
- package/scripts/automatic_advisory/cli.py +334 -0
- package/scripts/automatic_advisory/config/__init__.py +1 -0
- package/scripts/automatic_advisory/config/constants.py +93 -0
- package/scripts/automatic_advisory/config/timing.py +4 -0
- package/scripts/automatic_advisory/configuration.py +197 -0
- package/scripts/automatic_advisory/execution.py +350 -0
- package/scripts/automatic_advisory/git.py +69 -0
- package/scripts/automatic_advisory/model.py +107 -0
- package/scripts/automatic_advisory/process_host.py +32 -0
- package/scripts/automatic_advisory/publisher.py +84 -0
- package/scripts/automatic_advisory/runner.py +399 -0
- package/scripts/automatic_advisory/state.py +116 -0
- package/scripts/automatic_advisory/window_flags.py +41 -0
- package/scripts/automatic_advisory/windows_job.py +122 -0
- package/scripts/codex_compat_materializer.py +99 -13
- package/scripts/durable_post_lint.py +30 -2
- package/scripts/durable_post_lint_config/config/constants.py +10 -0
- package/scripts/durable_post_lint_config/config/test_constants.py +13 -0
- package/scripts/local_report_cli.py +184 -0
- package/scripts/local_report_core.py +247 -0
- package/scripts/local_report_publisher.py +12 -0
- package/scripts/local_report_validation.py +357 -0
- package/scripts/local_verification/__init__.py +23 -0
- package/scripts/local_verification/check_runner.py +364 -0
- package/scripts/local_verification/check_support.py +99 -0
- package/scripts/local_verification/cli.py +158 -0
- package/scripts/local_verification/command_runner.py +84 -0
- package/scripts/local_verification/config/__init__.py +1 -0
- package/scripts/local_verification/config/constants.py +89 -0
- package/scripts/local_verification/config/timing.py +1 -0
- package/scripts/local_verification/git_state.py +146 -0
- package/scripts/local_verification/manifest.py +224 -0
- package/scripts/local_verification/model.py +174 -0
- package/scripts/local_verification/runner.py +234 -0
- package/scripts/policy_lint/adapter_pairing.py +58 -3
- package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
- package/scripts/pr_verification/__init__.py +1 -0
- package/scripts/pr_verification/branch_refs.py +114 -0
- package/scripts/pr_verification/config/__init__.py +1 -0
- package/scripts/pr_verification/config/constants.py +84 -0
- package/scripts/pr_verification/config/timing.py +5 -0
- package/scripts/pr_verification/github.py +137 -0
- package/scripts/pr_verification/github_api.py +376 -0
- package/scripts/pr_verification/github_labels.py +88 -0
- package/scripts/pr_verification/github_parsing.py +182 -0
- package/scripts/pr_verification/github_transport.py +91 -0
- package/scripts/pr_verification/lock.py +122 -0
- package/scripts/pr_verification/model.py +43 -0
- package/scripts/pr_verification/requirements.txt +1 -0
- package/scripts/test_durable_post_lint.py +97 -0
- package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
- package/scripts/tests/test-audit-hooks.mjs +94 -2
- package/scripts/tests/test_automatic_advisory.py +376 -0
- package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
- package/scripts/tests/test_automatic_advisory_polling.py +216 -0
- package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
- package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
- package/scripts/tests/test_automatic_advisory_settings.py +48 -0
- package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
- package/scripts/tests/test_closed_pr_label.py +157 -0
- package/scripts/tests/test_codex_compat_materializer.py +189 -6
- package/scripts/tests/test_github_labels.py +101 -0
- package/scripts/tests/test_github_transport.py +125 -0
- package/scripts/tests/test_local_report_publisher.py +396 -0
- package/scripts/tests/test_local_report_publisher_cli.py +85 -0
- package/scripts/tests/test_local_verification.py +348 -0
- package/scripts/tests/test_local_verification_cli.py +98 -0
- package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
- package/scripts/tests/test_pr_verification_github.py +392 -0
- package/scripts/tests/test_pr_verification_lock.py +168 -0
- package/scripts/tests/test_verification_notice.py +347 -0
- package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
- package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
- package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
- package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
- package/hooks/blocking/destructive_command_blocker.py +0 -2073
- package/hooks/blocking/fable_spawn_gate.py +0 -187
- package/hooks/blocking/luna_fast_mode_gate.py +0 -160
- package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
- package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
- package/hooks/blocking/pii_prevention_blocker.py +0 -365
- package/hooks/blocking/piped_pytest_blocker.py +0 -1223
- package/hooks/blocking/question_to_user_enforcer.py +0 -130
- package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
- package/hooks/blocking/sensitive_file_protector.py +0 -167
- package/hooks/blocking/session_edit_stage_gate.py +0 -684
- package/hooks/blocking/session_handoff_blocker.py +0 -182
- package/hooks/blocking/shell_substitution_blocker.py +0 -350
- package/hooks/blocking/test_ask_user_question_shape.py +0 -69
- package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
- package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
- package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
- package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
- package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
- package/hooks/blocking/test_fable_spawn_gate.py +0 -381
- package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
- package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
- package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
- package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
- package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
- package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
- package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
- package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
- package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
- package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
- package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
- package/hooks/blocking/test_sensitive_file_protector.py +0 -365
- package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
- package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
- package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
- package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
- package/hooks/blocking/test_session_handoff_blocker.py +0 -318
- package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
- package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
- package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
- package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
- package/hooks/blocking/unscoped_search_blocker.py +0 -391
- package/hooks/blocking/write_existing_file_blocker.py +0 -124
- package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
- package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
- package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
- package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
- package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
- package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
- package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
- package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
- package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""Windows path detection tests for the PII prevention commit gate.
|
|
2
|
-
|
|
3
|
-
Guards the unquoted absolute Windows git path shape on the PowerShell surface::
|
|
4
|
-
|
|
5
|
-
is_git_commit_shell_command(r"C:\\tools\\git.exe commit")
|
|
6
|
-
ok: True -- the git.exe basename survives tokenization
|
|
7
|
-
flag: False -- backslash escaping mangles the basename token
|
|
8
|
-
|
|
9
|
-
An unquoted absolute path to the git binary must still be recognized as a
|
|
10
|
-
commit so a staged blob carrying PII cannot slip past the gate.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import importlib
|
|
16
|
-
import sys
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
|
|
19
|
-
_BLOCKING_DIRECTORY = Path(__file__).resolve().parent
|
|
20
|
-
if str(_BLOCKING_DIRECTORY) not in sys.path:
|
|
21
|
-
sys.path.insert(0, str(_BLOCKING_DIRECTORY))
|
|
22
|
-
|
|
23
|
-
_blocker_module = importlib.import_module("pii_prevention_blocker")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def test_unquoted_windows_git_exe_path_is_detected_as_commit() -> None:
|
|
27
|
-
assert _blocker_module.is_git_commit_shell_command(r"C:\tools\git.exe commit -m x")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_unquoted_windows_git_path_without_extension_is_detected() -> None:
|
|
31
|
-
assert _blocker_module.is_git_commit_shell_command(r"D:\bin\git commit -m note")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def test_quoted_windows_git_path_stays_detected_as_commit() -> None:
|
|
35
|
-
assert _blocker_module.is_git_commit_shell_command(
|
|
36
|
-
r'& "C:\Program Files\Git\cmd\git.exe" commit -m x'
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_unquoted_windows_git_status_is_not_a_commit() -> None:
|
|
41
|
-
assert not _blocker_module.is_git_commit_shell_command(r"C:\tools\git.exe status")
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
"""Behavior tests for the ephemeral-path skip on the Write/Edit PII surface.
|
|
2
|
-
|
|
3
|
-
Sub-defect (ii): the Write/Edit surface scanned files under an ephemeral scratch
|
|
4
|
-
root that sit outside every git repository. Such a draft only becomes durable
|
|
5
|
-
through ``gh --body-file`` or a staged commit, and each of those surfaces keeps
|
|
6
|
-
its own PII scan, so scanning the draft at write time is a pure false positive.
|
|
7
|
-
|
|
8
|
-
The write surface consults the shared ``is_ephemeral_path`` predicate directly.
|
|
9
|
-
These tests drive that real predicate on genuine ephemeral shapes: a harness
|
|
10
|
-
session scratchpad under the user temp directory, and a ``CLAUDE_JOB_DIR``
|
|
11
|
-
scratch tree. A write inside a git repository keeps full scanning, and the
|
|
12
|
-
durable-post and staged-commit surfaces stay gated even from ephemeral sources.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import shutil
|
|
18
|
-
import subprocess
|
|
19
|
-
import sys
|
|
20
|
-
import uuid
|
|
21
|
-
from collections.abc import Iterator
|
|
22
|
-
from pathlib import Path
|
|
23
|
-
|
|
24
|
-
import pytest
|
|
25
|
-
|
|
26
|
-
_HOOK_DIR = Path(__file__).parent
|
|
27
|
-
_HOOKS_DIR = _HOOK_DIR.parent
|
|
28
|
-
if str(_HOOK_DIR) not in sys.path:
|
|
29
|
-
sys.path.insert(0, str(_HOOK_DIR))
|
|
30
|
-
if str(_HOOKS_DIR) not in sys.path:
|
|
31
|
-
sys.path.insert(0, str(_HOOKS_DIR))
|
|
32
|
-
|
|
33
|
-
import tempfile # noqa: E402
|
|
34
|
-
|
|
35
|
-
from pii_payload_scan import evaluate_write_edit_payload # noqa: E402
|
|
36
|
-
from pii_prevention_blocker import evaluate, evaluate_bash_command # noqa: E402
|
|
37
|
-
|
|
38
|
-
from hooks_constants.code_rules_enforcer_constants import ( # noqa: E402
|
|
39
|
-
CLAUDE_JOB_DIR_ENVIRONMENT_VARIABLE_NAME,
|
|
40
|
-
CLAUDE_JOB_DIR_SCRATCH_SUBDIRECTORY,
|
|
41
|
-
EPHEMERAL_EXEMPT_DISABLE_ENVIRONMENT_VARIABLE_NAME,
|
|
42
|
-
)
|
|
43
|
-
from hooks_constants.harness_scratchpad_constants import ( # noqa: E402
|
|
44
|
-
HARNESS_SCRATCHPAD_LEAF_DIRECTORY_NAME,
|
|
45
|
-
HARNESS_SCRATCHPAD_USER_DIRECTORY_NAME,
|
|
46
|
-
HOOK_PAYLOAD_SESSION_ID_KEY,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _real_pii_email() -> str:
|
|
51
|
-
return "owner.fixture" + "@" + "acme-corp" + ".example" + ".io"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _bot_trailer_address() -> str:
|
|
55
|
-
return "noreply" + "@" + "anthropic" + ".com"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def _write_deny_reason(
|
|
59
|
-
target_path: Path,
|
|
60
|
-
sensitive_text: str,
|
|
61
|
-
hook_payload: dict[str, object] | None = None,
|
|
62
|
-
) -> str | None:
|
|
63
|
-
content = "owner contact " + sensitive_text + "\n"
|
|
64
|
-
return evaluate_write_edit_payload(
|
|
65
|
-
"Write",
|
|
66
|
-
{"file_path": str(target_path), "content": content},
|
|
67
|
-
hook_payload=hook_payload,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def _enable_job_dir_ephemeral_root(
|
|
72
|
-
monkeypatch: pytest.MonkeyPatch, job_directory: Path
|
|
73
|
-
) -> Path:
|
|
74
|
-
monkeypatch.delenv(EPHEMERAL_EXEMPT_DISABLE_ENVIRONMENT_VARIABLE_NAME, raising=False)
|
|
75
|
-
monkeypatch.setenv(CLAUDE_JOB_DIR_ENVIRONMENT_VARIABLE_NAME, str(job_directory))
|
|
76
|
-
ephemeral_root = job_directory / CLAUDE_JOB_DIR_SCRATCH_SUBDIRECTORY
|
|
77
|
-
ephemeral_root.mkdir(parents=True, exist_ok=True)
|
|
78
|
-
return ephemeral_root
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@pytest.fixture
|
|
82
|
-
def harness_scratchpad() -> Iterator[tuple[Path, str]]:
|
|
83
|
-
session_id = "session-" + uuid.uuid4().hex
|
|
84
|
-
mangled_working_directory = "cwd-" + uuid.uuid4().hex
|
|
85
|
-
temp_root = Path(tempfile.gettempdir())
|
|
86
|
-
user_root = temp_root / HARNESS_SCRATCHPAD_USER_DIRECTORY_NAME
|
|
87
|
-
scratchpad_root = (
|
|
88
|
-
user_root
|
|
89
|
-
/ mangled_working_directory
|
|
90
|
-
/ session_id
|
|
91
|
-
/ HARNESS_SCRATCHPAD_LEAF_DIRECTORY_NAME
|
|
92
|
-
)
|
|
93
|
-
scratchpad_root.mkdir(parents=True, exist_ok=True)
|
|
94
|
-
try:
|
|
95
|
-
yield scratchpad_root, session_id
|
|
96
|
-
finally:
|
|
97
|
-
try:
|
|
98
|
-
shutil.rmtree(user_root / mangled_working_directory)
|
|
99
|
-
except OSError:
|
|
100
|
-
pass
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def _init_repo_with_github_origin(repository_root: Path, origin_slug: str) -> None:
|
|
104
|
-
repository_root.mkdir(parents=True, exist_ok=True)
|
|
105
|
-
subprocess.run(["git", "init", "-q"], cwd=repository_root, check=True)
|
|
106
|
-
origin_url = "https://github.com/" + origin_slug + ".git"
|
|
107
|
-
subprocess.run(
|
|
108
|
-
["git", "remote", "add", "origin", origin_url],
|
|
109
|
-
cwd=repository_root,
|
|
110
|
-
check=True,
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def _init_repo_with_staged_email(repository_root: Path, staged_email: str) -> None:
|
|
115
|
-
repository_root.mkdir(parents=True, exist_ok=True)
|
|
116
|
-
subprocess.run(["git", "init", "-q"], cwd=repository_root, check=True)
|
|
117
|
-
subprocess.run(
|
|
118
|
-
["git", "config", "user.email", "dev@example.com"],
|
|
119
|
-
cwd=repository_root,
|
|
120
|
-
check=True,
|
|
121
|
-
)
|
|
122
|
-
subprocess.run(
|
|
123
|
-
["git", "config", "user.name", "Fixture Dev"],
|
|
124
|
-
cwd=repository_root,
|
|
125
|
-
check=True,
|
|
126
|
-
)
|
|
127
|
-
tracked_file = repository_root / "notes.md"
|
|
128
|
-
tracked_file.write_text("owner email " + staged_email + "\n", encoding="utf-8")
|
|
129
|
-
subprocess.run(["git", "add", "notes.md"], cwd=repository_root, check=True)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def test_windows_shaped_scratchpad_write_is_skipped(
|
|
133
|
-
harness_scratchpad: tuple[Path, str],
|
|
134
|
-
) -> None:
|
|
135
|
-
scratchpad_root, session_id = harness_scratchpad
|
|
136
|
-
scratch_target = scratchpad_root / "blocker-matrix.txt"
|
|
137
|
-
payload = {HOOK_PAYLOAD_SESSION_ID_KEY: session_id}
|
|
138
|
-
assert _write_deny_reason(scratch_target, _bot_trailer_address(), payload) is None
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def test_write_with_no_scannable_texts_returns_none(tmp_path: Path) -> None:
|
|
142
|
-
target_path = tmp_path / "empty-write.md"
|
|
143
|
-
assert (
|
|
144
|
-
evaluate_write_edit_payload(
|
|
145
|
-
"Write",
|
|
146
|
-
{"file_path": str(target_path), "content": ""},
|
|
147
|
-
)
|
|
148
|
-
is None
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def test_entry_point_threads_payload_session_id_to_scratchpad_skip(
|
|
153
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
154
|
-
harness_scratchpad: tuple[Path, str],
|
|
155
|
-
) -> None:
|
|
156
|
-
scratchpad_root, session_id = harness_scratchpad
|
|
157
|
-
monkeypatch.delenv("CLAUDE_CODE_SESSION_ID", raising=False)
|
|
158
|
-
scratch_target = scratchpad_root / "blocker-matrix.txt"
|
|
159
|
-
content = "owner contact " + _real_pii_email() + "\n"
|
|
160
|
-
payload = {
|
|
161
|
-
"tool_name": "Write",
|
|
162
|
-
"tool_input": {"file_path": str(scratch_target), "content": content},
|
|
163
|
-
HOOK_PAYLOAD_SESSION_ID_KEY: session_id,
|
|
164
|
-
}
|
|
165
|
-
assert evaluate(payload) is None
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def test_job_dir_ephemeral_write_outside_repo_is_skipped(
|
|
169
|
-
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
170
|
-
) -> None:
|
|
171
|
-
ephemeral_root = _enable_job_dir_ephemeral_root(monkeypatch, tmp_path)
|
|
172
|
-
scratch_target = ephemeral_root / "drafts" / "draft.md"
|
|
173
|
-
scratch_target.parent.mkdir(parents=True)
|
|
174
|
-
assert _write_deny_reason(scratch_target, _real_pii_email()) is None
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def test_write_inside_ordinary_repository_is_still_denied(
|
|
178
|
-
tmp_path: Path,
|
|
179
|
-
) -> None:
|
|
180
|
-
repository_root = tmp_path / "repo"
|
|
181
|
-
_init_repo_with_github_origin(repository_root, "SomeOwner/some-repo")
|
|
182
|
-
deny_reason = _write_deny_reason(repository_root / "notes.md", _real_pii_email())
|
|
183
|
-
assert deny_reason is not None
|
|
184
|
-
assert "email" in deny_reason
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def test_ephemeral_path_inside_a_repository_is_still_denied(
|
|
188
|
-
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
189
|
-
) -> None:
|
|
190
|
-
ephemeral_root = _enable_job_dir_ephemeral_root(monkeypatch, tmp_path)
|
|
191
|
-
repository_root = ephemeral_root / "repo"
|
|
192
|
-
_init_repo_with_github_origin(repository_root, "SomeOwner/some-repo")
|
|
193
|
-
deny_reason = _write_deny_reason(repository_root / "notes.md", _real_pii_email())
|
|
194
|
-
assert deny_reason is not None
|
|
195
|
-
assert "email" in deny_reason
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def test_gh_body_file_from_ephemeral_path_is_still_scanned(
|
|
199
|
-
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
200
|
-
) -> None:
|
|
201
|
-
ephemeral_root = _enable_job_dir_ephemeral_root(monkeypatch, tmp_path)
|
|
202
|
-
body_path = ephemeral_root / "pr_body.md"
|
|
203
|
-
body_path.write_text("contact " + _real_pii_email() + "\n", encoding="utf-8")
|
|
204
|
-
command = 'gh pr comment 12 --body-file "' + str(body_path) + '"'
|
|
205
|
-
deny_reason = evaluate_bash_command(command, working_directory=None)
|
|
206
|
-
assert deny_reason is not None
|
|
207
|
-
assert "email" in deny_reason
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def test_staged_commit_from_ephemeral_tree_is_still_scanned(
|
|
211
|
-
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
212
|
-
) -> None:
|
|
213
|
-
ephemeral_root = _enable_job_dir_ephemeral_root(monkeypatch, tmp_path)
|
|
214
|
-
repository_root = ephemeral_root / "repo"
|
|
215
|
-
_init_repo_with_staged_email(repository_root, _real_pii_email())
|
|
216
|
-
deny_reason = evaluate_bash_command(
|
|
217
|
-
"git commit -m test", working_directory=str(repository_root)
|
|
218
|
-
)
|
|
219
|
-
assert deny_reason is not None
|
|
220
|
-
assert "email" in deny_reason
|
|
221
|
-
assert "staged commit" in deny_reason
|