own_binary_fork_guard_test reads binary cache files and errors with invalid byte sequence #671

Closed
opened 2026-09-24 14:13:15 +00:00 by jared · 2 comments
Owner

Problem

plugins/os-sdlc/tests/own_binary_fork_guard_test.rb globs plugins/{*/tests,cc-architect/scripts/plugin_config/test}/**/* and calls File.read(path) then String#match? on every file.
On a checkout where Python tests have run, plugins/*/tests/__pycache__/*.pyc files exist (gitignored).
Reading them yields a string with invalid UTF-8 and match? raises ArgumentError.
The suite on main shows 1 error while the same commit in a fresh worktree is green.

Observed

OsSdlcOwnBinaryForkGuardTest#test_only_allowlisted_files_fork_a_repo_binary:
ArgumentError: invalid byte sequence in UTF-8
    tests/own_binary_fork_guard_test.rb:48:in 'String#match?'

Reproduce

Run any plugin's pytest suite so pycache exists, then ruby tests/all.rb in plugins/os-sdlc.

Approach

Restrict the glob to source extensions (rb, py, sh) or skip files where content.valid_encoding? is false.

  • Narrow candidate_files or guard forks_a_repo_binary?
  • Suite green on a checkout with pycache present

Acceptance criteria

  • ruby tests/all.rb on main with pycache dirs present: 0 errors

Origin

Session: 6d6f7dfd-b7cc-41b1-a401-71f83f4d5c77
Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/6d6f7dfd-b7cc-41b1-a401-71f83f4d5c77.jsonl
Where: plugins/os-sdlc/tests/own_binary_fork_guard_test.rb:48
Parent: #642
VERDICT: CREATE — real reproducible test-environment bug with root cause identified; not speculative hardening.

## Problem plugins/os-sdlc/tests/own_binary_fork_guard_test.rb globs `plugins/{*/tests,cc-architect/scripts/plugin_config/test}/**/*` and calls `File.read(path)` then `String#match?` on every file. On a checkout where Python tests have run, `plugins/*/tests/__pycache__/*.pyc` files exist (gitignored). Reading them yields a string with invalid UTF-8 and `match?` raises ArgumentError. The suite on main shows 1 error while the same commit in a fresh worktree is green. ## Observed ``` OsSdlcOwnBinaryForkGuardTest#test_only_allowlisted_files_fork_a_repo_binary: ArgumentError: invalid byte sequence in UTF-8 tests/own_binary_fork_guard_test.rb:48:in 'String#match?' ``` ## Reproduce Run any plugin's pytest suite so __pycache__ exists, then `ruby tests/all.rb` in plugins/os-sdlc. ## Approach Restrict the glob to source extensions (rb, py, sh) or skip files where `content.valid_encoding?` is false. - [ ] Narrow candidate_files or guard forks_a_repo_binary? - [ ] Suite green on a checkout with __pycache__ present ## Acceptance criteria - [ ] `ruby tests/all.rb` on main with __pycache__ dirs present: 0 errors ## Origin Session: 6d6f7dfd-b7cc-41b1-a401-71f83f4d5c77 Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/6d6f7dfd-b7cc-41b1-a401-71f83f4d5c77.jsonl Where: plugins/os-sdlc/tests/own_binary_fork_guard_test.rb:48 Parent: #642 VERDICT: CREATE — real reproducible test-environment bug with root cause identified; not speculative hardening.
Author
Owner

The bug

Checkout __pycache__/*.pyc under plugins/*/tests? ruby tests/all.rb
Fresh worktree no green
main on a machine that ran pytest yes (gitignored) 1 error

The error every run on such a machine:

OsSdlcOwnBinaryForkGuardTest#test_only_allowlisted_files_fork_a_repo_binary:
  ArgumentError: invalid byte sequence in UTF-8
      tests/own_binary_fork_guard_test.rb:48:in 'String#match?'

Why it happens

plugins/os-sdlc/tests/own_binary_fork_guard_test.rb scans every file under the plugin test trees:

# before
TEST_TREE_GLOB = "#{PLUGINS_ROOT}/{*/tests,cc-architect/scripts/plugin_config/test}/**/*"

def forks_a_repo_binary?(path)
  content = File.read(path)
  content.match?(SPAWN_PATTERN) && content.match?(binary_reference_pattern)
end

File.read returns a UTF-8 tagged string for a .pyc file. The bytes are not valid UTF-8. String#match? raises on the first invalid byte.

The fix

Only source files can spawn a process. Restrict the glob to source extensions:

# after
TEST_TREE_GLOB = "#{PLUGINS_ROOT}/{*/tests,cc-architect/scripts/plugin_config/test}/**/*.{rb,py,sh}"

One line. The scan never opens a .pyc, .json, or fixture binary again.

Why this shape and not valid_encoding?

Option Reads binaries? Guards future non-UTF-8 fixtures? Lines
A: narrow glob to *.{rb,py,sh} no yes, by never opening them 1
B: next unless content.valid_encoding? yes, then discards yes 1

Option A is the recommendation. It states the test's real scope. A fork can only come from a source file. Option B keeps reading files that cannot be offenders.

Verify

  1. Confirm __pycache__ dirs exist: find plugins -path '*tests/__pycache__' -type d.
  2. Run ruby tests/all.rb in plugins/os-sdlc.
  3. Expected result: 0 errors. The allowlist assertion still runs over all .rb, .py, and .sh test files.
## The bug | Checkout | `__pycache__/*.pyc` under `plugins/*/tests`? | `ruby tests/all.rb` | | --- | --- | --- | | Fresh worktree | no | green | | `main` on a machine that ran pytest | yes (gitignored) | 1 error | The error every run on such a machine: ``` OsSdlcOwnBinaryForkGuardTest#test_only_allowlisted_files_fork_a_repo_binary: ArgumentError: invalid byte sequence in UTF-8 tests/own_binary_fork_guard_test.rb:48:in 'String#match?' ``` ## Why it happens `plugins/os-sdlc/tests/own_binary_fork_guard_test.rb` scans every file under the plugin test trees: ```ruby # before TEST_TREE_GLOB = "#{PLUGINS_ROOT}/{*/tests,cc-architect/scripts/plugin_config/test}/**/*" def forks_a_repo_binary?(path) content = File.read(path) content.match?(SPAWN_PATTERN) && content.match?(binary_reference_pattern) end ``` `File.read` returns a UTF-8 tagged string for a `.pyc` file. The bytes are not valid UTF-8. `String#match?` raises on the first invalid byte. ## The fix Only source files can spawn a process. Restrict the glob to source extensions: ```ruby # after TEST_TREE_GLOB = "#{PLUGINS_ROOT}/{*/tests,cc-architect/scripts/plugin_config/test}/**/*.{rb,py,sh}" ``` One line. The scan never opens a `.pyc`, `.json`, or fixture binary again. ## Why this shape and not `valid_encoding?` | Option | Reads binaries? | Guards future non-UTF-8 fixtures? | Lines | | --- | --- | --- | --- | | A: narrow glob to `*.{rb,py,sh}` | no | yes, by never opening them | 1 | | B: `next unless content.valid_encoding?` | yes, then discards | yes | 1 | Option A is the recommendation. It states the test's real scope. A fork can only come from a source file. Option B keeps reading files that cannot be offenders. ## Verify 1. Confirm `__pycache__` dirs exist: `find plugins -path '*tests/__pycache__' -type d`. 2. Run `ruby tests/all.rb` in `plugins/os-sdlc`. 3. Expected result: 0 errors. The allowlist assertion still runs over all `.rb`, `.py`, and `.sh` test files.
Author
Owner

Resolution

Done: candidate_files now uses git ls-files over the two test pathspecs instead of Dir.glob, so gitignored pycache/*.pyc and other untracked files are never read

Evidence: commit 6ee5fe3c on main; ruby tests/all.rb in plugins/os-sdlc with 4 tests/pycache dirs present: 1419 runs, 0 failures, 0 errors

Follow-ups: none

## Resolution **Done:** candidate_files now uses git ls-files over the two test pathspecs instead of Dir.glob, so gitignored __pycache__/*.pyc and other untracked files are never read **Evidence:** commit 6ee5fe3c on main; ruby tests/all.rb in plugins/os-sdlc with 4 tests/__pycache__ dirs present: 1419 runs, 0 failures, 0 errors **Follow-ups:** none
jared closed this issue 2026-09-24 14:35:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#671
No description provided.