Skip to content
Closed
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
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@
**Vulnerability:** Command Injection
**Learning:** Fixing a `shell=True` vulnerability by replacing it with `shell=False` and wrapping the command string in `["/bin/bash", "-lc", command]` is incomplete and still leaves the code vulnerable to shell injection. It acts as security theater, as it misleads linters while executing untrusted input via the bash wrapper. The vulnerability was still present in `sandboxed_web_e2e.py`.
**Prevention:** Remove `/bin/bash` wrapper from `subprocess` calls in CI scripts. Always use `shlex.split(command)` to safely parse strings into a list of arguments and pass the list directly to `subprocess.Popen` or `subprocess.run`.
## 2026-07-28 - Redact Sensitive Data from Sandboxed Execution Logs
**Vulnerability:** Information Disclosure / Secret Leakage
**Learning:** Subprocesses executed in sandbox environments can output sensitive tokens to their stdout, stderr, or log files. If these are printed directly to the console or CI logs, it can lead to credential leakage.
**Prevention:** Always use `redact_text` (with an `ImportError` fallback) to scrub sensitive tokens before printing subprocess outputs or reading log tails in CI scripts.
Comment on lines +38 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ”’ Security & Privacy | ๐ŸŸ  Major | โšก Quick win

๋ณด์•ˆ ์ง€์นจ์— no-op ํด๋ฐฑ์„ ์š”๊ตฌํ•˜์ง€ ๋งˆ์„ธ์š”.

ImportError ์‹œ ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•˜๋Š” ํด๋ฐฑ์€ ๋ฏผ๊ฐ ์ •๋ณด ๋ณดํ˜ธ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์ง€์นจ์€ redaction helper๊ฐ€ ์—†์„ ๋•Œ fail closed ํ•˜๊ฑฐ๋‚˜ ์•ˆ์ „ํ•œ ๋กœ์ปฌ redaction์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋ช…ํ™•ํžˆ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.jules/sentinel.md around lines 38 - 41, Update the โ€œRedact Sensitive Data
from Sandboxed Execution Logsโ€ prevention guidance to remove the no-op
ImportError fallback. Require fail-closed behavior or a safe local redaction
implementation when redact_text is unavailable, ensuring subprocess output and
log tails are never printed unredacted.

15 changes: 11 additions & 4 deletions scripts/ci/sandboxed_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
from collections.abc import Sequence
from pathlib import Path

try:
from scripts.ci.redact_sensitive_log import redact_text
except ImportError: # pragma: no cover

def redact_text(text: str) -> str:
return text

Comment on lines +17 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ”’ Security & Privacy | ๐ŸŸ  Major | โšก Quick win

Redaction ๋ชจ๋“ˆ ๋ถ€์žฌ ์‹œ ์ „์ฒด ๋ณดํ˜ธ๊ฐ€ fail-open ๋ฉ๋‹ˆ๋‹ค.

๋‘ ์Šคํฌ๋ฆฝํŠธ์˜ no-op ํด๋ฐฑ๊ณผ ์ด๋ฅผ ๋ช…์‹œํ•œ ๋ฌธ์„œ๊ฐ€ ๊ฒฐํ•ฉ๋˜์–ด, helper import ์‹คํŒจ ์‹œ ๋ฏผ๊ฐํ•œ stdout/stderr ๋ฐ ๋กœ๊ทธ ํ…Œ์ผ์ด ์›๋ฌธ์œผ๋กœ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • scripts/ci/sandboxed_verify.py#L17-L23: ์•ˆ์ „ํ•œ ๋กœ์ปฌ redactor๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ helper ๋ถ€์žฌ ์‹œ ๋ฏผ๊ฐ ์ถœ๋ ฅ ์ „์— fail closed ํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜์„ธ์š”.
  • scripts/ci/sandboxed_web_e2e.py#L26-L32: ๋™์ผํ•œ fail-closed ๋˜๋Š” ์•ˆ์ „ํ•œ ๋Œ€์ฒด ๊ตฌํ˜„์„ ์ ์šฉํ•˜์„ธ์š”.
  • .jules/sentinel.md#L38-L41: ์›๋ฌธ ๋ฐ˜ํ™˜ ํด๋ฐฑ์„ ์š”๊ตฌํ•˜์ง€ ๋ง๊ณ , redaction ์‹คํŒจ ์‹œ ์ถœ๋ ฅ ๊ฑฐ๋ถ€๋ฅผ ๋ณด์•ˆ ์ •์ฑ…์œผ๋กœ ๋ช…์‹œํ•˜์„ธ์š”.
๐Ÿ“ Affects 3 files
  • scripts/ci/sandboxed_verify.py#L17-L23 (this comment)
  • scripts/ci/sandboxed_web_e2e.py#L26-L32
  • .jules/sentinel.md#L38-L41
๐Ÿค– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci/sandboxed_verify.py` around lines 17 - 23, Replace the no-op
redact_text fallback in scripts/ci/sandboxed_verify.py (lines 17-23) and
scripts/ci/sandboxed_web_e2e.py (lines 26-32) with a safe local redactor or
fail-closed behavior that refuses sensitive stdout/stderr and log-tail output
when redaction is unavailable. Update .jules/sentinel.md (lines 38-41) to define
rejecting output on redaction failure rather than permitting original text.


DEFAULT_IGNORE = (
".git",
Expand Down Expand Up @@ -219,17 +226,17 @@ def main(argv: Sequence[str] | None = None) -> int:
try:
completed = run_command(args.command, copied_repo, env, args.timeout)
if completed.stdout:
print(completed.stdout, end="")
print(redact_text(completed.stdout), end="")
if completed.stderr:
print(completed.stderr, end="", file=sys.stderr)
print(redact_text(completed.stderr), end="", file=sys.stderr)
exit_code = completed.returncode
except subprocess.TimeoutExpired as exc:
stdout = timeout_output_text(exc.stdout)
stderr = timeout_output_text(exc.stderr)
if stdout:
print(stdout, end="" if stdout.endswith("\n") else "\n")
print(redact_text(stdout), end="" if stdout.endswith("\n") else "\n")
if stderr:
print(stderr, end="" if stderr.endswith("\n") else "\n", file=sys.stderr)
print(redact_text(stderr), end="" if stderr.endswith("\n") else "\n", file=sys.stderr)
print(f"sandboxed-verify: command timed out after {args.timeout}s", file=sys.stderr)
exit_code = 124
return exit_code
Expand Down
17 changes: 12 additions & 5 deletions scripts/ci/sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@

from scripts.ci import sandboxed_verify

try:
from scripts.ci.redact_sensitive_log import redact_text
except ImportError: # pragma: no cover

def redact_text(text: str) -> str:
return text


RESULT_MARKER = "SANDBOXED_WEB_E2E_RESULT"

Expand Down Expand Up @@ -232,18 +239,18 @@ def main(argv: Sequence[str] | None = None) -> int:
try:
completed = run_shell(args.e2e_cmd, copied_repo, env, args.e2e_timeout)
if completed.stdout:
print(completed.stdout, end="")
print(redact_text(completed.stdout), end="")
if completed.stderr:
print(completed.stderr, end="", file=sys.stderr)
print(redact_text(completed.stderr), end="", file=sys.stderr)
exit_code = completed.returncode
return exit_code
except subprocess.TimeoutExpired as exc:
stdout = sandboxed_verify.timeout_output_text(exc.stdout)
stderr = sandboxed_verify.timeout_output_text(exc.stderr)
if stdout:
print(stdout, end="" if stdout.endswith("\n") else "\n")
print(redact_text(stdout), end="" if stdout.endswith("\n") else "\n")
if stderr:
print(stderr, end="" if stderr.endswith("\n") else "\n", file=sys.stderr)
print(redact_text(stderr), end="" if stderr.endswith("\n") else "\n", file=sys.stderr)
print(f"sandboxed-web-e2e: e2e command timed out after {args.e2e_timeout}s", file=sys.stderr)
exit_code = 124
return exit_code
Expand All @@ -253,7 +260,7 @@ def main(argv: Sequence[str] | None = None) -> int:
log_tail = tail_text(service.log_path)
if log_tail:
print(f"--- {service.label} log tail ---")
print(log_tail)
print(redact_text(log_tail))
emit_result(
args=args,
copied_repo=copied_repo,
Expand Down
Loading