Skip to content

Catch a countless head/tail in the truncation guard (rebased #24) - #27

Open
chaosisnotrandomitisrhythmic wants to merge 1 commit into
AnswerDotAI:mainfrom
chaosisnotrandomitisrhythmic:upstream-bare-head-v2
Open

chaosisnotrandomitisrhythmic wants to merge 1 commit into
AnswerDotAI:mainfrom
chaosisnotrandomitisrhythmic:upstream-bare-head-v2

Conversation

@chaosisnotrandomitisrhythmic

Copy link
Copy Markdown

Reopens #24, rebased onto current main after the test rewrite in 328369a. The earlier PR touched test_bash_guard, which no longer exists, so it could not have merged as written. This version adds one small focused test instead.

_TRUNC requires an explicit line count, so a bare head or tail at the end of a pipe never matches:

_TRUNC = re.compile(r'\|\s*(tail|head)\s+(-n\s*)?-?([1-9]|1[0-9])\b')

The \s+ and the digit class are both mandatory. Both commands keep 10 lines with no count, which is the same cut as the -10 form the guard already rejects, so the rule names the two commands it is about and then misses them whenever the count is left off.

command lines kept current guard
tar tzf x.tgz | head -10 10 rejected
tar tzf x.tgz | head 10 allowed
ls ~ | tail 10 allowed

The fix

Two alternatives instead of one: an explicit count under 20, or no count at all.

_TRUNC = re.compile(r'\|\s*(?:tail|head)'
                    r'(?:\s+(?:-n\s*)?-?(?:[1-9]|1[0-9])\b'  # explicit: -5, -n 5, -19
                    r'|(?=\s*(?:$|[|;&\n])))')                # bare: defaults to 10

The countless branch is a lookahead anchored to a real command boundary: end of string, or before |, ;, & or a newline. That keeps head -30, head -c 200, tail -f and ls | header out of it.

Evidence it matters

Over eight days of one developer's sessions this guard fired 200 times on truncating pipes. In the same corpus a rejected | head -1 was retried moments later as | sed -n 1p and passed, which is the same shape of hole: the rule matches a notation rather than the behaviour. This PR closes the head/tail half, where the bypass is the guard's own two commands. sed -n '1,10p' and awk 'NR<=10' truncate identically and remain allowed; neither is what the message tells you to fix, so they feel like a separate question.

Tests

test_bash_guard_bare_head_tail, seven assertions, all failing before the change. It re-imports bash_guard_msg, which the test rewrite dropped.

Note on running the suite

uv run pytest cannot resolve dependencies on this checkout, before and after the change:

Because the requested Python version (>=3.10) does not satisfy Python>=3.11 and
llmdojo>=0.0.3 depends on Python>=3.11, we can conclude that llmdojo>=0.0.3 cannot be used.

pyproject.toml declares requires-python = ">=3.10" while llmdojo needs >=3.11. I verified bash_guard_msg directly instead, which needs only the standard library. Happy to send the requires-python bump as its own PR if it is wanted.


🤖 Generated with Claude Code

_TRUNC requires an explicit line count, so a bare `head` or `tail` at the end of
a pipe never matches. Both keep 10 lines by default, the same cut as the `-10`
form the guard already rejects. The rule names the two commands it is about and
then misses them whenever the count is left off.

The regex now has two alternatives: an explicit count under 20, or no count at
all. The countless branch is a lookahead anchored to a real command boundary, so
counts of 20 and above, byte counts, follow mode, and words that merely start
with those four letters are all unaffected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant