Skip to content

Fix shell injection via workspace path in _peek_file_lines - #119

Merged
wilke merged 1 commit into
mainfrom
fix/shell-injection-workspace-path
Sep 1, 2026
Merged

Fix shell injection via workspace path in _peek_file_lines#119
wilke merged 1 commit into
mainfrom
fix/shell-injection-workspace-path

Conversation

@wilke

@wilke wilke commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

$source is a submitter-chosen workspace path — input_file, dna_file, rna_file or msa_file straight from the job parameters — and it was interpolated into a shell string:

my $cmd = "p3-cat '$source' 2>/dev/null | head -n $n_lines";
@lines = `$cmd`;

A single quote in the workspace object name escapes the quoting and the remainder runs as the service user. Confirmed against the real sub with a harmless payload:

/ws/home/x'; touch /tmp/PWNED; echo '.fasta

Replaced with list-form open(my $ph, "-|", "p3-cat", $source), so the path is a single argv element with no shell involved. Behaviour preserved: reading only $n_lines and closing early sends SIGPIPE to p3-cat, which is what actually stops the transfer — previously the job of the piped head.

Credit: found by the coconut-stabilinnator session, which hit the same pattern in App-StabiliNNator.pl (a converter invoked through backticks with a path ending in a workspace basename, fixed before merge) and suggested checking here.

New tests/test_service_script.py — one test asserts the code carries no shell form (ignoring comments), one executes the extracted sub with a hostile path and a stub p3-cat and asserts the payload did not run. Mutation-tested: restoring the old line fails both.

$source is a submitter-chosen workspace path -- input_file, dna_file,
rna_file or msa_file straight from the job parameters -- and it was
interpolated into a shell string:

    my $cmd = "p3-cat '$source' 2>/dev/null | head -n $n_lines";
    @lines = `$cmd`;

A single quote in the workspace object name escapes the quoting and the
rest of the name runs as the service user. Confirmed with a harmless
payload against the real sub:

    /ws/home/x'; touch /tmp/PWNED; echo '.fasta

Replaced with list-form open, so the path is one argv element and there
is no shell to escape from. Behaviour is preserved: reading only
$n_lines and closing early sends SIGPIPE to p3-cat, which is what stops
the transfer -- previously the job of the piped `head`.

Found by coconut-stabilinnator, who hit the same pattern in
App-StabiliNNator.pl (a converter invoked through backticks with a path
ending in a workspace basename) and suggested checking here.

Adds tests/test_service_script.py: one asserting the code carries no
shell form, one executing the sub with a hostile path and a stub p3-cat
and checking the payload did not run. Both fail against the old line.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEWo4xvL59PHC1QUvtTV9F
@wilke
wilke merged commit a593591 into main Sep 1, 2026
1 check passed
@wilke
wilke deleted the fix/shell-injection-workspace-path branch September 1, 2026 18:19
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