Skip to content

Commit a83eb80

Browse files
committed
Check settings files and --settings for a permission mode
The security check looked for defaultMode only in an inline 'settings' input. Also read the file a 'settings' input or a --settings flag in claude_args names, when it is inside the repository, and fail if it sets a permission mode.
1 parent b1922f2 commit a83eb80

1 file changed

Lines changed: 32 additions & 5 deletions

File tree

‎.github/scripts/check_workflow_hardening.py‎

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,41 @@ def permission_mode_problem(step: dict, exempt: bool) -> str | None:
141141
f"'claude_args' has '--permission-mode {mode}'. "
142142
"Change it to '--permission-mode auto'"
143143
)
144-
if "defaultMode" in str(inputs.get("settings", "")):
145-
return (
146-
"remove 'defaultMode' from the step's 'settings': "
147-
"'settings' must not set a permission mode"
148-
)
144+
settings = [("the step's 'settings'", inputs.get("settings", ""))]
145+
settings += [
146+
("'--settings' in 'claude_args'", args[index + 1])
147+
for index, arg in enumerate(args)
148+
if arg == "--settings" and index + 1 < len(args)
149+
]
150+
settings += [
151+
("'--settings' in 'claude_args'", arg.split("=", 1)[1])
152+
for arg in args
153+
if arg.startswith("--settings=")
154+
]
155+
for where, value in settings:
156+
if "defaultMode" in settings_text(value):
157+
return f"remove 'defaultMode' from {where}: settings must not set a permission mode"
149158
return None
150159

151160

161+
def settings_text(value) -> str:
162+
"""The settings JSON a 'settings' value stands for: the value itself, or the contents of
163+
the file it names when it is a path inside the repository."""
164+
text = str(value or "").strip()
165+
if not text or text.startswith("{"):
166+
return text
167+
path = pathlib.Path(text)
168+
root = pathlib.Path.cwd().resolve()
169+
try:
170+
resolved = path.resolve()
171+
resolved.relative_to(root)
172+
except (OSError, ValueError):
173+
return text
174+
if resolved.is_file():
175+
return resolved.read_text(encoding="utf-8", errors="replace")
176+
return text
177+
178+
152179
def check_job(file_name: str, job_id: str, job: dict) -> list[str]:
153180
key = f"{file_name}:{job_id}"
154181
where = f".github/workflows/{file_name}: job '{job_id}'"

0 commit comments

Comments
 (0)