-
Notifications
You must be signed in to change notification settings - Fork 3
fix: 2026-07-02 audit remediation — date-safe redaction, DR incomplete flags, symlink-safe installs #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: 2026-07-02 audit remediation — date-safe redaction, DR incomplete flags, symlink-safe installs #21
Changes from all commits
efd481c
43ed70a
f856aa8
7e9dd2e
b97e42a
3d6be94
5e611c2
333f2a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,21 @@ | |
| # PII patterns. | ||
| _EMAIL_RE = re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+") | ||
| _PHONE_RE = re.compile(r"\+?\d[\d ()\-]{8,}\d") | ||
| # Calendar dates satisfy _PHONE_RE ("2026-05-26" is 10 digit/dash chars), so a | ||
| # phone match that *starts* with a date shape keeps the date — dates in | ||
| # memories/tasks/conversations vastly outnumber phone numbers written with a | ||
| # leading date. Only the date itself is preserved; the rest of the match is | ||
| # re-scanned so "2026-05-26 617-555-0123" still masks the phone. | ||
| _DATE_PREFIX_RE = re.compile(r"^(\d{4}-\d{2}-\d{2}|\d{1,2}-\d{1,2}-\d{4})(?=$|[^\d])") | ||
|
|
||
|
|
||
| def _phone_repl(m: re.Match) -> str: | ||
| text = m.group(0) | ||
| dm = _DATE_PREFIX_RE.match(text) | ||
| if not dm: | ||
| return "<PHONE>" | ||
| prefix = dm.group(1) | ||
| return prefix + _PHONE_RE.sub(_phone_repl, text[len(prefix):]) | ||
|
Comment on lines
+14
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Where is redact() invoked, and on what kind of content?
rg -nP '\bredact\s*\(' -g '!tests/**' -C2Repository: robotlearning123/gpt2agent Length of output: 164 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- gpt2agent/tools/_redact.py ---'
cat -n gpt2agent/tools/_redact.py
echo
echo '--- usages of redact / _phone_repl / _DATE_PREFIX_RE / _PHONE_RE ---'
rg -n "_phone_repl|_DATE_PREFIX_RE|_PHONE_RE|\bredact\b" gpt2agent -g '!tests/**' -C 2Repository: robotlearning123/gpt2agent Length of output: 12486 🏁 Script executed: python3 - <<'PY'
import re, sys
_PHONE_RE = re.compile(r"\+?\d[\d ()\-]{8,}\d")
_DATE_PREFIX_RE = re.compile(r"^(\d{4}-\d{2}-\d{2}|\d{1,2}-\d{1,2}-\d{4})(?=$|[^\d])")
calls = 0
max_depth = 0
depth = 0
def _phone_repl(m: re.Match) -> str:
global calls, max_depth, depth
calls += 1
depth += 1
max_depth = max(max_depth, depth)
try:
text = m.group(0)
dm = _DATE_PREFIX_RE.match(text)
if not dm:
return "<PHONE>"
prefix = dm.group(1)
return prefix + _PHONE_RE.sub(_phone_repl, text[len(prefix):])
finally:
depth -= 1
def redact(s: str) -> str:
return _PHONE_RE.sub(_phone_repl, s)
print("recursionlimit=", sys.getrecursionlimit())
for n in [1, 2, 3, 10, 50, 100, 200, 500, 900, 1000, 1100]:
calls = max_depth = depth = 0
s = " ".join([f"2026-05-{i:02d}" for i in range(1, n+1)])
try:
out = redact(s)
print(n, "ok", "calls=", calls, "max_depth=", max_depth, "len(out)=", len(out))
except RecursionError as e:
print(n, "RecursionError", "calls=", calls, "max_depth=", max_depth, type(e).__name__)
break
PYRepository: robotlearning123/gpt2agent Length of output: 683 🏁 Script executed: python3 - <<'PY'
import re, sys
_PHONE_RE = re.compile(r"\+?\d[\d ()\-]{8,}\d")
_DATE_PREFIX_RE = re.compile(r"^(\d{4}-\d{2}-\d{2}|\d{1,2}-\d{1,2}-\d{4})(?=$|[^\d])")
calls = 0
max_depth = 0
depth = 0
def _phone_repl(m: re.Match) -> str:
global calls, max_depth, depth
calls += 1
depth += 1
max_depth = max(max_depth, depth)
try:
text = m.group(0)
dm = _DATE_PREFIX_RE.match(text)
if not dm:
return "<PHONE>"
prefix = dm.group(1)
return prefix + _PHONE_RE.sub(_phone_repl, text[len(prefix):])
finally:
depth -= 1
def redact(s: str) -> str:
return _PHONE_RE.sub(_phone_repl, s)
def valid_dates(n):
# Cycle within valid month/day ranges to keep every token date-shaped.
months = [f"{m:02d}" for m in range(1, 13)]
days = [f"{d:02d}" for d in range(1, 29)]
out = []
i = 0
for _ in range(n):
out.append(f"2026-{months[i % 12]}-{days[i % 28]}")
i += 1
return " ".join(out)
print("recursionlimit=", sys.getrecursionlimit())
for n in [1, 2, 3, 10, 50, 100, 200, 500, 800, 900, 950, 1000, 1050, 1100]:
calls = max_depth = depth = 0
s = valid_dates(n)
try:
out = redact(s)
print(n, "ok", "calls=", calls, "max_depth=", max_depth, "len(out)=", len(out))
except RecursionError as e:
print(n, "RecursionError", "calls=", calls, "max_depth=", max_depth, type(e).__name__)
break
PYRepository: robotlearning123/gpt2agent Length of output: 549 Avoid recursive re-entry in 🤖 Prompt for AI Agents |
||
|
|
||
| # Secret patterns. Users routinely paste API keys / tokens into ChatGPT, so they | ||
| # end up in memories, tasks, and custom instructions — which these tools return | ||
|
|
@@ -25,5 +40,5 @@ def redact(s: object) -> object: | |
| s = _APIKEY_RE.sub("<APIKEY>", s) | ||
| s = _GH_TOKEN_RE.sub("<TOKEN>", s) | ||
| s = _EMAIL_RE.sub("<EMAIL>", s) | ||
| s = _PHONE_RE.sub("<PHONE>", s) | ||
| s = _PHONE_RE.sub(_phone_repl, s) | ||
| return s | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Incomplete note never fires for timeout-only case.
The PR objective is to warn when a report "may be incomplete due to timeout or abnormal termination," but the gate on line 192 (
if truncated:) and line 255 only checksterminated_abnormally. If adoneevent setstimeout: truewithoutterminated_abnormally,timed_outis computed but never consulted for the append decision — the note is silently skipped. Same bug in bothdeep_research(line 192) anddeep_research_heavy(line 255).This matches the regression tests too: only
terminated_abnormally=Trueis exercised; there's no test asserting the note appears when onlytimeoutis set.🐛 Proposed fix (apply to both tools)
Also applies to: 192-194, 214-225, 255-257
🤖 Prompt for AI Agents