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-08-04 - Prevent Secret Leakage in Subprocess Error Traces
**Vulnerability:** ์ •๋ณด ๋…ธ์ถœ / ์‹œํฌ๋ฆฟ ์œ ์ถœ (Information Disclosure / Secret Leakage)
**Learning:** ์„œ๋ธŒํ”„๋กœ์„ธ์Šค ๋ช…๋ น์–ด๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋˜๊ฑฐ๋‚˜ ์‹คํŒจํ•  ๋•Œ(์˜ˆ: `TimeoutExpired`), ์บก์ฒ˜๋œ `stdout` ๋ฐ `stderr`๋ฅผ ๋‹จ์ˆœํžˆ ์ถœ๋ ฅํ•˜๊ฒŒ ๋˜๋ฉด CI ๋กœ๊ทธ์— ๋ฏผ๊ฐํ•œ ์ž๊ฒฉ ์ฆ๋ช…, API ํ‚ค ๋˜๋Š” ํ† ํฐ์ด ์˜๋„์น˜ ์•Š๊ฒŒ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. redaction ๋„๊ตฌ์— ๋Œ€ํ•ด `ImportError` ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์— ์˜์กดํ•  ๊ฒฝ์šฐ, ์กฐ์šฉํžˆ ์‹คํŒจํ•˜์—ฌ redaction ๊ณผ์ •์„ ์šฐํšŒํ•  ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.
**Prevention:** ์„œ๋ธŒํ”„๋กœ์„ธ์Šค ์ถœ๋ ฅ์ด๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผ ์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ์ „์—๋Š” ํ•ญ์ƒ `scripts.ci.redact_sensitive_log.redact_text`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฏผ๊ฐํ•œ ํ† ํฐ์„ ์Šคํฌ๋Ÿฌ๋น™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. redaction ๋ชจ๋“ˆ์ด ๋ฌด์กฐ๊ฑด์ ์œผ๋กœ ์ž„ํฌํŠธ๋˜๋„๋ก ๋ณด์žฅํ•˜์—ฌ(์˜ˆ: `sys.path`์— ์ €์žฅ์†Œ ๋ฃจํŠธ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜์—ฌ), ์ž„ํฌํŠธ ์‹คํŒจ ์‹œ ์•ˆ์ „ํ•˜๊ฒŒ ์‹œ์Šคํ…œ์„ ์ข…๋ฃŒํ•˜๊ณ  ํ•„ํ„ฐ๋ง๋˜์ง€ ์•Š์€ ๋กœ๊ทธ๊ฐ€ ๋…ธ์ถœ๋  ๊ฐ€๋Šฅ์„ฑ์„ ์›์ฒœ ์ฐจ๋‹จํ•˜์‹ญ์‹œ์˜ค.
11 changes: 7 additions & 4 deletions scripts/ci/sandboxed_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
from collections.abc import Sequence
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2]))

from scripts.ci.redact_sensitive_log import redact_text

DEFAULT_IGNORE = (
".git",
Expand Down Expand Up @@ -169,8 +172,8 @@ def timeout_output_text(value: str | bytes | None) -> str:
if value is None:
return ""
if isinstance(value, bytes):
return value.decode(errors="replace")
return value
return redact_text(value.decode(errors="replace"))
return redact_text(value)


def emit_result(
Expand Down Expand Up @@ -219,9 +222,9 @@ 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)
Expand Down
6 changes: 3 additions & 3 deletions scripts/ci/sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))

from scripts.ci import sandboxed_verify

from scripts.ci.redact_sensitive_log import redact_text

RESULT_MARKER = "SANDBOXED_WEB_E2E_RESULT"

Expand Down Expand Up @@ -232,9 +232,9 @@ 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:
Expand Down
2 changes: 2 additions & 0 deletions tests/test_sandboxed_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
assert sandboxed_verify.timeout_output_text(None) == ""
assert sandboxed_verify.timeout_output_text(b"byte-output") == "byte-output"
assert sandboxed_verify.timeout_output_text("text-output") == "text-output"
assert sandboxed_verify.timeout_output_text("my ghp_123456789012345678901234567890123456 token") == "my [REDACTED] token"

Check warning

Code scanning / Gitleaks

Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. Warning test

github-pat has detected secret for file tests/test_sandboxed_verify.py at commit 59c2c58d59bb37463a5704130d44c23f172f9afd.
assert sandboxed_verify.timeout_output_text(b"my ghp_123456789012345678901234567890123456 token") == "my [REDACTED] token"

Check warning

Code scanning / Gitleaks

Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. Warning test

github-pat has detected secret for file tests/test_sandboxed_verify.py at commit 59c2c58d59bb37463a5704130d44c23f172f9afd.


def test_main_runs_command_in_copy_without_mutating_source(tmp_path, capsys):
Expand Down
10 changes: 6 additions & 4 deletions tests/test_sandboxed_web_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@
return sandboxed_web_e2e.Service(label, command, DoneProcess(), log_path)

def fake_run_shell(command, cwd, env, timeout):
raise subprocess.TimeoutExpired(command, timeout, output=b"e2e-out", stderr=b"e2e-err")
raise subprocess.TimeoutExpired(command, timeout, output=b"e2e-out ghp_123456789012345678901234567890123456", stderr=b"e2e-err")

Check warning

Code scanning / Gitleaks

Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. Warning test

github-pat has detected secret for file tests/test_sandboxed_web_e2e.py at commit 59c2c58d59bb37463a5704130d44c23f172f9afd.

monkeypatch.setattr(sandboxed_web_e2e, "start_service", fake_start)
monkeypatch.setattr(sandboxed_web_e2e, "wait_for_url", lambda url, timeout, service: True)
Expand All @@ -423,7 +423,8 @@
captured = capsys.readouterr()

assert exit_code == 124
assert "e2e-out" in captured.out
assert "e2e-out [REDACTED]" in captured.out
assert "ghp_123456789012345678901234567890123456" not in captured.out

Check warning

Code scanning / Gitleaks

Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. Warning test

github-pat has detected secret for file tests/test_sandboxed_web_e2e.py at commit 59c2c58d59bb37463a5704130d44c23f172f9afd.
assert "e2e-err" in captured.err
assert "e2e command timed out after 3s" in captured.err

Expand Down Expand Up @@ -470,7 +471,7 @@
repo.mkdir()

def fake_run_shell(command, cwd, env, timeout):
raise subprocess.TimeoutExpired(command, timeout, output="e2e-out", stderr="e2e-err")
raise subprocess.TimeoutExpired(command, timeout, output="e2e-out ghp_123456789012345678901234567890123456", stderr="e2e-err")

Check warning

Code scanning / Gitleaks

Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. Warning test

github-pat has detected secret for file tests/test_sandboxed_web_e2e.py at commit 59c2c58d59bb37463a5704130d44c23f172f9afd.

monkeypatch.setattr(sandboxed_web_e2e, "run_shell", fake_run_shell)

Expand All @@ -491,7 +492,8 @@
captured = capsys.readouterr()

assert exit_code == 124
assert "e2e-out" in captured.out
assert "e2e-out [REDACTED]" in captured.out
assert "ghp_123456789012345678901234567890123456" not in captured.out

Check warning

Code scanning / Gitleaks

Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure. Warning test

github-pat has detected secret for file tests/test_sandboxed_web_e2e.py at commit 59c2c58d59bb37463a5704130d44c23f172f9afd.
assert "e2e-err" in captured.err
assert "e2e command timed out after 1s" in captured.err
assert "SANDBOXED_WEB_E2E_RESULT" in captured.out
Expand Down
Loading