From 7c2d2db6a182b97ae288a182c169fd38f4cb08a0 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 17 May 2026 18:46:07 +0200 Subject: [PATCH] verify-action-build: treat `Authorization: token/Bearer ...` as data-fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit manusa/actions-setup-minikube@v2.17.0 has a thin axios wrapper in src/github.js that fetches GitHub release metadata as JSON: return axios({method: 'GET', ...options, url, headers}); The file is a transport-layer helper — no data-parse markers, no binary-handling markers — so `_file_is_pure_data_fetch` didn't exempt it and the axios call landed in the unverified-download list. Authorization headers with `token ` or `Bearer ` prefixes are an unambiguous "authenticated API call" signal: public release-asset downloads from S3 / CDN / GitHub Releases don't use bearer auth. Add that as a data-parse marker so transport-layer API helpers stop producing false positives. Real binary handlers in the same file still trip `_JS_BINARY_HANDLE_PATTERNS` and override the exemption. Surfaced while triaging apache/infrastructure-actions#848. Generated-by: Claude Opus 4.7 (1M context) --- .../verify_action_build/test_security.py | 67 +++++++++++++++++++ utils/verify_action_build/security.py | 11 +++ 2 files changed, 78 insertions(+) diff --git a/utils/tests/verify_action_build/test_security.py b/utils/tests/verify_action_build/test_security.py index ed241733a..c6f94fcff 100644 --- a/utils/tests/verify_action_build/test_security.py +++ b/utils/tests/verify_action_build/test_security.py @@ -1085,6 +1085,73 @@ def test_accept_application_json_double_quoted_also_works(self): """ assert _file_is_pure_data_fetch(content) is True + # The exact pattern that triggered the false positive on PR #848 + # (manusa/actions-setup-minikube@v2.17.0): a thin axios wrapper that + # sets ``headers.Authorization = `token ${githubToken}`` and returns + # the raw response. The file is a transport-layer helper — no + # data-parse markers, no binary handling — but the Authorization + # header is an unambiguous "authenticated API call" signal. + MANUSA_GITHUB_API_HELPER = """\ +'use strict'; + +const axios = require('axios'); + +const apiBaseUrl = process.env.GITHUB_API_URL || 'https://api.github.com'; +const serverBaseUrl = process.env.GITHUB_SERVER_URL || 'https://github.com'; + +const gitHubRequest = async ({url, githubToken, options = {}}) => { + const headers = {}; + if (githubToken) { + headers.Authorization = `token ${githubToken}`; + } + return axios({method: 'GET', ...options, url, headers}); +}; + +module.exports = {gitHubRequest, apiBaseUrl, serverBaseUrl}; +""" + + def test_authorization_token_header_exempts(self): + # axios call + Authorization: `token ${...}` → API client shape, + # not a binary download. + assert _file_is_pure_data_fetch(self.MANUSA_GITHUB_API_HELPER) is True + assert _find_binary_downloads_js(self.MANUSA_GITHUB_API_HELPER) == [] + + def test_authorization_bearer_header_exempts(self): + # The other half of the same family — Bearer-prefixed auth. + bearer_client = """\ +const axios = require('axios'); +async function call(url, jwt) { + return axios({ + method: 'GET', + url, + headers: { Authorization: `Bearer ${jwt}` }, + }); +} +""" + assert _file_is_pure_data_fetch(bearer_client) is True + assert _find_binary_downloads_js(bearer_client) == [] + + def test_authorization_with_extract_in_same_file_not_exempt(self): + # An API helper that *also* extracts a downloaded binary in the + # same file must keep the strict check — auth alone shouldn't + # whitewash a real binary handler. + mixed = """\ +const axios = require('axios'); +const tc = require('@actions/tool-cache'); + +async function fetchManifest(token) { + return axios({ url: 'https://api.example.com/manifest', + headers: { Authorization: `Bearer ${token}` } }); +} + +const archive = await tc.downloadTool('https://x/foo.tar.gz'); +await tc.extractTar(archive, dest); +""" + assert _file_is_pure_data_fetch(mixed) is False + findings = _find_binary_downloads_js(mixed) + # Both the axios call and the downloadTool stay flagged. + assert len(findings) == 2 + class TestFunctionDefinitionNotADownload: """Regression: ``async function downloadTool(...)`` is a function diff --git a/utils/verify_action_build/security.py b/utils/verify_action_build/security.py index ee5c59460..6998d5292 100644 --- a/utils/verify_action_build/security.py +++ b/utils/verify_action_build/security.py @@ -1153,6 +1153,17 @@ def analyze_action_metadata( # graalvm/setup-graalvm's GDS metadata calls # (``http.get(requestUrl, { accept: 'application/json' })``) hit this. re.compile(r"['\"`]accept['\"`]\s*:\s*['\"`]application/json", re.IGNORECASE), + # Authorization header with token/Bearer auth: the file is making + # an authenticated API call, not pulling a public binary. Public + # release-asset downloads from S3/CDN/GitHub Releases don't use + # bearer auth, so this is a strong "API client, not downloader" + # signal. manusa/actions-setup-minikube's ``src/github.js`` helper + # — a thin axios wrapper that sets ``headers.Authorization = + # `token ${githubToken}`` and returns the raw response — hits this. + re.compile( + r"\bAuthorization\b\s*[:=]\s*[`\"'](?:token |Bearer )", + re.IGNORECASE, + ), ] # Markers indicating the response is treated as a binary or executable —