Skip to content

sync-upstream: ${FROM}/${TO} are expanded into an unquoted python heredoc (CWE-78 sibling) #13

Description

@jobordu

Split out of #12, where CodeRabbit flagged the same class in a different step.

The exposure

Bump pin in pyproject.toml does:

python3 - <<EOF
from pathlib import Path
p = Path("pyproject.toml")
text = p.read_text()
text = text.replace(
    'upstream-version = "${FROM}"',
    'upstream-version = "${TO}"',
    1,
)
...
EOF

The heredoc delimiter is unquoted (<<EOF, not <<'EOF'), so the shell expands ${FROM} and ${TO} into the Python source before python runs. They are not Python variables — they are textual substitution into a program.

to_version originates outside this repo: scripts/refresh-upstream.sh reads it from the upstream release feed. A version string containing a single quote terminates the Python string literal; one containing a newline plus arbitrary statements injects code that runs with the workflow's contents: write token.

Same root cause as the one fixed in #12 — an externally-sourced value pasted in as syntax rather than passed as a value — just a different substrate (Python source instead of a shell command line).

Why it was not fixed in #12

The remedy changes the bump logic itself (quote the heredoc, pass the values via os.environ), and the workflow is currently dead (#11), so that edit cannot be exercised end-to-end. Every other change in #12 is mutation-verified; adding an untestable edit to it would have weakened the whole PR. Filing instead of quietly absorbing.

Suggested fix

FROM="$FROM" TO="$TO" python3 - <<'PY'
import os, re
from pathlib import Path
frm, to = os.environ["FROM"], os.environ["TO"]
p = Path("pyproject.toml")
text = p.read_text()
text = text.replace(f'upstream-version = "{frm}"', f'upstream-version = "{to}"', 1)
text = text.replace(f'"latitudesh-python-sdk=={frm}"', f'"latitudesh-python-sdk=={to}"', 1)
p.write_text(text)
PY

Quoted delimiter (<<'PY') stops shell expansion entirely; os.environ keeps the values as data.

Worth pairing with

A sanity check on to_version right where it is produced, so nothing downstream has to be defensive — upstream versions should match something like ^[0-9]+(\.[0-9]+)*([a-z0-9.\-]*)$, and anything else should fail the run loudly rather than flow into a shell, a Python heredoc, and a git ref name.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions