Skip to content
Merged
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
14 changes: 13 additions & 1 deletion s03_permission/README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ def check_deny_list(command: str) -> str | None:
**ゲート 2**:ルールマッチング — 「いつユーザーに聞くべきか」を記述する。各ルールはツールとチェック条件を指定する。

```python
import re

DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)

def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))

PERMISSION_RULES = [
{
"tools": ["read_file", "write_file", "edit_file"],
Expand All @@ -65,7 +74,9 @@ PERMISSION_RULES = [
},
{
"tools": ["bash"],
"check": lambda args: any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
"check": lambda args: contains_destructive_command(args.get("command", "")) or any(
kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]
),
"message": "Potentially destructive command",
},
]
Expand Down Expand Up @@ -141,6 +152,7 @@ python s03_permission/code.py
2. `Delete the file test.txt`(bash + rm でゲート 2 が発動)
3. `What files are in the current directory?`(読み取り専用、すべて通過)
4. `Try to write a file to /etc/something`(作業ディレクトリ外への書き込みでゲート 2 が発動)
5. Windows では `del test.txt` と `DEL test.txt` がゲート 2 を発動し、`model`、`delimiter`、`echo del test.txt` は発動しない。

観察のポイント:どの操作がそのまま通過するか? どれに確認が必要か? どれが即座に拒否されるか?

Expand Down
14 changes: 13 additions & 1 deletion s03_permission/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ def check_deny_list(command: str) -> str | None:
**Gate 2**: Rule matching — describes "when to ask the user." Each rule specifies a tool and a check condition.

```python
import re

DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)

def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))

PERMISSION_RULES = [
{
"tools": ["read_file", "write_file", "edit_file"],
Expand All @@ -65,7 +74,9 @@ PERMISSION_RULES = [
},
{
"tools": ["bash"],
"check": lambda args: any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
"check": lambda args: contains_destructive_command(args.get("command", "")) or any(
kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]
),
"message": "Potentially destructive command",
},
]
Expand Down Expand Up @@ -141,6 +152,7 @@ Try these prompts:
2. `Delete the file test.txt` (bash + rm triggers Gate 2)
3. `What files are in the current directory?` (read-only, all pass)
4. `Try to write a file to /etc/something` (writing outside workspace triggers Gate 2)
5. On Windows, `del test.txt` and `DEL test.txt` trigger Gate 2, while `model`, `delimiter`, and `echo del test.txt` do not.

What to watch for: Which operations pass through? Which need your confirmation? Which are denied outright?

Expand Down
14 changes: 13 additions & 1 deletion s03_permission/README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ def check_deny_list(command: str) -> str | None:
**闸门 2**负责规则匹配,用来描述"什么时候需要问用户"。每条规则指定工具和检查条件。

```python
import re

DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)

def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))

PERMISSION_RULES = [
{
"tools": ["read_file", "write_file", "edit_file"],
Expand All @@ -65,7 +74,9 @@ PERMISSION_RULES = [
},
{
"tools": ["bash"],
"check": lambda args: any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
"check": lambda args: contains_destructive_command(args.get("command", "")) or any(
kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]
),
"message": "Potentially destructive command",
},
]
Expand Down Expand Up @@ -141,6 +152,7 @@ python s03_permission/code.py
2. `Delete the file test.txt`(bash + rm 会触发闸门 2)
3. `What files are in the current directory?`(只读,全部通过)
4. `Try to write a file to /etc/something`(写工作区外,触发闸门 2)
5. 在 Windows 上,`del test.txt` 和 `DEL test.txt` 会触发闸门 2,而 `model`、`delimiter` 和 `echo del test.txt` 不会。

观察重点:哪些操作直接通过?哪些需要你确认?哪些被直接拒绝?

Expand Down
13 changes: 12 additions & 1 deletion s03_permission/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"""

import os
import re
import subprocess
from pathlib import Path

Expand Down Expand Up @@ -152,12 +153,22 @@ def check_deny_list(command: str) -> str | None:


# Gate 2: Rule matching - context-dependent checks
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


PERMISSION_RULES = [
{"tools": ["read_file", "write_file", "edit_file"],
"check": lambda args: not (WORKDIR / args.get("path", "")).resolve().is_relative_to(WORKDIR),
"message": "Writing outside workspace"},
{"tools": ["bash"],
"check": lambda args: any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
"check": lambda args: contains_destructive_command(args.get("command", "")) or
any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
"message": "Potentially destructive command"},
]

Expand Down
27 changes: 19 additions & 8 deletions s04_hooks/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"""

import os
import re
import subprocess
from pathlib import Path

Expand Down Expand Up @@ -139,22 +140,32 @@ def trigger_hooks(event: str, *args):

# s03 permission check logic, now wrapped as a hook
DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if="]
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)
DESTRUCTIVE = ["rm ", "> /etc/", "chmod 777"]


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


def permission_hook(block):
"""PreToolUse: s03 check_permission() logic moved here."""
if block.name == "bash":
command = block.input.get("command", "")
for pattern in DENY_LIST:
if pattern in block.input.get("command", ""):
if pattern in command:
print(f"\n\033[31m[blocked] '{pattern}'\033[0m")
return "Permission denied by deny list"
for kw in DESTRUCTIVE:
if kw in block.input.get("command", ""):
print(f"\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"
if contains_destructive_command(command) or any(
kw in command for kw in DESTRUCTIVE
):
print(f"\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"
if block.name in ("read_file", "write_file", "edit_file"):
path = block.input.get("path", "")
if not (WORKDIR / path).resolve().is_relative_to(WORKDIR):
Expand Down
24 changes: 17 additions & 7 deletions s05_todo_write/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import ast
import json
import os
import re
import subprocess
from pathlib import Path

Expand Down Expand Up @@ -218,8 +219,16 @@ def trigger_hooks(event: str, *args):
return None

DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if="]
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)
DESTRUCTIVE = ["rm ", "> /etc/", "chmod 777"]


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


def permission_hook(block):
"""PreToolUse: s03 permission logic, registered as an s04 hook."""
if block.name == "bash":
Expand All @@ -228,13 +237,14 @@ def permission_hook(block):
if pattern in command:
print(f"\n\033[31m[blocked] '{pattern}'\033[0m")
return "Permission denied by deny list"
for keyword in DESTRUCTIVE:
if keyword in command:
print(f"\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"
if contains_destructive_command(command) or any(
keyword in command for keyword in DESTRUCTIVE
):
print(f"\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"
if block.name in ("read_file", "write_file", "edit_file"):
path = block.input.get("path", "")
if not (WORKDIR / path).resolve().is_relative_to(WORKDIR):
Expand Down
23 changes: 16 additions & 7 deletions s06_subagent/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"""

import os
import re
import subprocess
from pathlib import Path

Expand Down Expand Up @@ -154,9 +155,16 @@ def trigger_hooks(event: str, *args):


DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if="]
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)
DESTRUCTIVE = ["rm ", "> /etc/", "chmod 777"]


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


def permission_hook(block):
"""PreToolUse: block denied operations and ask about risky ones."""
if block.name == "bash":
Expand All @@ -165,13 +173,14 @@ def permission_hook(block):
if pattern in command:
print(f"\n\033[31m[blocked] '{pattern}'\033[0m")
return "Permission denied by deny list"
for keyword in DESTRUCTIVE:
if keyword in command:
print("\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"
if contains_destructive_command(command) or any(
keyword in command for keyword in DESTRUCTIVE
):
print("\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"

if block.name in ("read_file", "write_file", "edit_file"):
path = block.input.get("path", "")
Expand Down
23 changes: 16 additions & 7 deletions s07_skill_loading/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"""

import os
import re
import subprocess
from pathlib import Path

Expand Down Expand Up @@ -241,9 +242,16 @@ def trigger_hooks(event: str, *args):


DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if="]
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)
DESTRUCTIVE = ["rm ", "> /etc/", "chmod 777"]


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


def permission_hook(block):
"""PreToolUse: block denied operations and ask about risky ones."""
if block.name == "bash":
Expand All @@ -252,13 +260,14 @@ def permission_hook(block):
if pattern in command:
print(f"\n\033[31m[blocked] '{pattern}'\033[0m")
return "Permission denied by deny list"
for keyword in DESTRUCTIVE:
if keyword in command:
print("\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"
if contains_destructive_command(command) or any(
keyword in command for keyword in DESTRUCTIVE
):
print("\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
choice = input(" Allow? [y/N] ").strip().lower()
if choice not in ("y", "yes"):
return "Permission denied by user"

if block.name in ("read_file", "write_file", "edit_file"):
path = block.input.get("path", "")
Expand Down
11 changes: 10 additions & 1 deletion s08_context_compact/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,25 @@ def trigger_hooks(event: str, *args):


DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if="]
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)
DESTRUCTIVE = ["rm ", "> /etc/", "chmod 777"]


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


def permission_hook(block):
if block.name == "bash":
command = block.input.get("command", "")
for pattern in DENY_LIST:
if pattern in command:
return f"Permission denied by deny list: {pattern}"
if any(keyword in command for keyword in DESTRUCTIVE):
if contains_destructive_command(command) or any(
keyword in command for keyword in DESTRUCTIVE
):
print("\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
if input(" Allow? [y/N] ").strip().lower() not in ("y", "yes"):
Expand Down
12 changes: 11 additions & 1 deletion s09_memory/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,15 +634,25 @@ def trigger_hooks(event: str, *args):
return None

DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if="]
DESTRUCTIVE_COMMAND_WORD = re.compile(
r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)
DESTRUCTIVE = ["rm ", "> /etc/", "chmod 777"]


def contains_destructive_command(command: str) -> bool:
return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


def permission_hook(block):
if block.name == "bash":
command = block.input.get("command", "")
for pattern in DENY_LIST:
if pattern in command:
return f"Permission denied by deny list: {pattern}"
if any(keyword in command for keyword in DESTRUCTIVE):
if contains_destructive_command(command) or any(
keyword in command for keyword in DESTRUCTIVE
):
print("\n\033[33m[permission] Potentially destructive command\033[0m")
print(f" Tool: {block.name}({block.input})")
if input(" Allow? [y/N] ").strip().lower() not in ("y", "yes"):
Expand Down
Loading