From e3bff1921d7b8deb9c882c3639f954e0ab32317e Mon Sep 17 00:00:00 2001 From: Mike Odnis Date: Sun, 10 May 2026 09:17:24 -0400 Subject: [PATCH] fix(api-docs/sisters): close template-injection on ref in cpp/dotnet/python/typescript Mirrors the rust-template fix (#70) across the remaining four sister templates. Each has the same `inputs.ref || github.ref_name` inlined into a single-quoted shell literal at template-expansion time. Tag names containing a single quote (Git permits them) would break out of the literal and run arbitrary shell with the runner's checkout and DOCS_REPO_PR_TOKEN in scope. Routes the value through a step-level env: REF_RAW: instead. Each template's existing slug-substitution rules are preserved (cpp uses \@/-, typescript uses \@/, dotnet/python only normalize /); only the source of $raw changes from interpolated literal to env var. DOCS_REF_NAME / DOCS_REF_SLUG outputs are byte-identical for every input ref. Behavior unchanged. --- automation/source-repo-templates/api-docs.cpp.yml | 11 ++++++++++- automation/source-repo-templates/api-docs.dotnet.yml | 11 ++++++++++- automation/source-repo-templates/api-docs.python.yml | 11 ++++++++++- .../source-repo-templates/api-docs.typescript.yml | 11 ++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/automation/source-repo-templates/api-docs.cpp.yml b/automation/source-repo-templates/api-docs.cpp.yml index 3aa82260..55c11b0b 100644 --- a/automation/source-repo-templates/api-docs.cpp.yml +++ b/automation/source-repo-templates/api-docs.cpp.yml @@ -45,8 +45,17 @@ jobs: steps: - name: Resolve ref metadata # Single source of truth for the ref this run documents. + # + # The ref is routed through env: instead of being inlined via + # ${{ }}. Inlining at template-expansion time would interpolate + # the raw string into the shell literal, so a tag name with a + # single quote (Git allows it) could break out of the quoted + # context. Env indirection keeps user-controlled data on the + # variable side of the shell parser, where it cannot escape. + env: + REF_RAW: ${{ inputs.ref || github.ref_name }} run: | - raw='${{ inputs.ref || github.ref_name }}' + raw="$REF_RAW" raw="${raw#refs/tags/}" raw="${raw#refs/heads/}" slug="${raw//\//-}" diff --git a/automation/source-repo-templates/api-docs.dotnet.yml b/automation/source-repo-templates/api-docs.dotnet.yml index 6aadbbb1..da7da875 100644 --- a/automation/source-repo-templates/api-docs.dotnet.yml +++ b/automation/source-repo-templates/api-docs.dotnet.yml @@ -68,8 +68,17 @@ jobs: # workflow_dispatch can pass an alternate ref via inputs.ref; # fall back to github.ref_name (already stripped of refs/...). # DOCS_REF_SLUG is branch-safe for use in PR/branch names. + # + # The ref is routed through env: instead of being inlined via + # ${{ }}. Inlining at template-expansion time would interpolate + # the raw string into the shell literal, so a tag name with a + # single quote (Git allows it) could break out of the quoted + # context. Env indirection keeps user-controlled data on the + # variable side of the shell parser, where it cannot escape. + env: + REF_RAW: ${{ inputs.ref || github.ref_name }} run: | - raw='${{ inputs.ref || github.ref_name }}' + raw="$REF_RAW" raw="${raw#refs/tags/}" raw="${raw#refs/heads/}" slug="${raw//\//-}" diff --git a/automation/source-repo-templates/api-docs.python.yml b/automation/source-repo-templates/api-docs.python.yml index 95916d53..24173ed0 100644 --- a/automation/source-repo-templates/api-docs.python.yml +++ b/automation/source-repo-templates/api-docs.python.yml @@ -67,8 +67,17 @@ jobs: # workflow_dispatch can pass an alternate ref via inputs.ref; # fall back to github.ref_name (already stripped of refs/...). # DOCS_REF_SLUG is branch-safe for use in PR/branch names. + # + # The ref is routed through env: instead of being inlined via + # ${{ }}. Inlining at template-expansion time would interpolate + # the raw string into the shell literal, so a tag name with a + # single quote (Git allows it) could break out of the quoted + # context. Env indirection keeps user-controlled data on the + # variable side of the shell parser, where it cannot escape. + env: + REF_RAW: ${{ inputs.ref || github.ref_name }} run: | - raw='${{ inputs.ref || github.ref_name }}' + raw="$REF_RAW" raw="${raw#refs/tags/}" raw="${raw#refs/heads/}" slug="${raw//\//-}" diff --git a/automation/source-repo-templates/api-docs.typescript.yml b/automation/source-repo-templates/api-docs.typescript.yml index ef6cc7ee..26a46bb6 100644 --- a/automation/source-repo-templates/api-docs.typescript.yml +++ b/automation/source-repo-templates/api-docs.typescript.yml @@ -68,8 +68,17 @@ jobs: # fall back to github.ref_name (already stripped of refs/...). # DOCS_REF_SLUG is branch-safe for use in PR/branch names # (`@resq-sw/ui@v0.35.6` → `resq-sw-ui-v0.35.6`). + # + # The ref is routed through env: instead of being inlined via + # ${{ }}. Inlining at template-expansion time would interpolate + # the raw string into the shell literal, so a tag name with a + # single quote (Git allows it) could break out of the quoted + # context. Env indirection keeps user-controlled data on the + # variable side of the shell parser, where it cannot escape. + env: + REF_RAW: ${{ inputs.ref || github.ref_name }} run: | - raw='${{ inputs.ref || github.ref_name }}' + raw="$REF_RAW" raw="${raw#refs/tags/}" raw="${raw#refs/heads/}" slug="${raw//\//-}"