ci: security review: pass prompt as system prompt
What changed, and why it matters
This commit only changes an internal CI helper script that runs an AI security review on pull requests. It moves the review instructions from the user message to the system prompt so the AI prioritizes them over large diffs. There is no change to Electrum wallet code, cryptography, networking, or any user-facing behavior.
No security action required. This is a benign CI script improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors contrib/ci/claude_security_review.py to read the static security-review instructions from contrib/ci/security_review_prompt.md and pass them via a new –append-system-prompt CLI flag, while the dynamic diff/commit data becomes the user prompt. The prompt markdown is updated to refer to the diff as being in the ‘user message’ instead of ‘below’. This is a tooling/prompt-engineering change with no effect on the Electrum application or its build artifacts.
Changed components
contrib/ci/claude_security_review.pycontrib/ci/security_review_prompt.mdInspect captured patch +16 / −12
diff --git a/contrib/ci/claude_security_review.py b/contrib/ci/claude_security_review.py
old mode 100755
new mode 100644
index 7324bff..342bf7a
--- a/contrib/ci/claude_security_review.py
+++ b/contrib/ci/claude_security_review.py
@@ -80,20 +80,22 @@ def changed_files_from_diff(diff: str) -> str:
)
-def build_prompt(diff: str, changed_files: str, commit_messages: str) -> str:
+def read_system_prompt() -> str:
with open(PROMPT_FILE) as f:
- instructions = f.read()
+ return f.read()
+
+def build_user_prompt(diff: str, changed_files: str, commit_messages: str) -> str:
return (
- f"{instructions}\n\n"
- f"---\n\n"
+ "Review the following PR diff according to the review "
+ "guidelines in your system prompt.\n\n"
f"## Changed files\n\n```\n{changed_files}\n```\n\n"
f"## Commit messages\n\n```\n{commit_messages}\n```\n\n"
f"## Diff\n\n```diff\n{diff}\n```"
)
-def run_claude(prompt: str) -> str | None:
+def run_claude(user_prompt: str, system_prompt: str) -> str | None:
"""Invoke Claude Code CLI in print mode. Returns review text or None on failure.
Passes the prompt via stdin to avoid OS argument length limits (MAX_ARG_STRLEN).
@@ -105,12 +107,13 @@ def run_claude(prompt: str) -> str | None:
"--model", CLAUDE_MODEL,
"--effort", CLAUDE_EFFORT,
"--output-format", "text",
+ "--append-system-prompt", system_prompt,
]
try:
result = subprocess.run(
cmd,
- input=prompt,
+ input=user_prompt,
capture_output=True,
text=True,
timeout=CLAUDE_TIMEOUT_SECONDS,
@@ -237,10 +240,11 @@ def main() -> int:
print(f"ERROR: diff is {len(diff)} chars, exceeds maximum of {MAX_DIFF_CHARS}. Skipping review.")
return 2
- prompt = build_prompt(diff, changed_files, commit_messages)
+ user_prompt = build_user_prompt(diff, changed_files, commit_messages)
+ system_prompt = read_system_prompt()
print(f"\nRunning Claude Code review (model: {CLAUDE_MODEL})...\n")
- review = run_claude(prompt)
+ review = run_claude(user_prompt, system_prompt)
if review is None:
print("Review failed to produce output.")
diff --git a/contrib/ci/security_review_prompt.md b/contrib/ci/security_review_prompt.md
index 5a417d4..e6fbce3 100644
--- a/contrib/ci/security_review_prompt.md
+++ b/contrib/ci/security_review_prompt.md
@@ -8,10 +8,10 @@ developer time and erodes trust in this review.
## Scope
-Focus your findings on the diff provided below -- only flag issues introduced or worsened by
-changes in this PR. You have access to the full Electrum codebase; use it freely to read
-surrounding code, trace call chains, and understand what the diff actually does. But do not
-audit code outside the diff -- the codebase is context, not the review target.
+Focus your findings on the diff provided in the user message -- only flag issues introduced
+or worsened by changes in this PR. You have access to the full Electrum codebase; use it
+freely to read surrounding code, trace call chains, and understand what the diff actually
+does. But do not audit code outside the diff -- the codebase is context, not the review target.
Focus on changes that introduce, worsen, or fail to mitigate security vulnerabilities.
Only flag issues introduced or worsened by the diff. Do not flag
pre-existing issues visible in context lines unless the change makes them newly exploitable.
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.