{"id":"CVE-2026-113732","kind":"vulnerability","aliases":["CVE-2026-113732","GHSA-c947-3pg5-gm8q"],"related":[],"title":"virtualenv activate.fish runs commands from a path or prompt with a backslash before a quote","description":"### Impact\n\n`FishActivator` defines no `quote()`, so it inherits `ViaTemplateActivator.quote`, which returns `shlex.quote(string)`. That function follows POSIX `sh` rules, where a backslash inside single quotes stays a literal character. fish reads `\\'` inside single quotes as an escaped quote and `\\\\` as an escaped backslash. A value with a backslash before a quote therefore ends the literal somewhere other than where `shlex.quote` put the end, and fish expands the command substitution that follows.\n\nThe environment path reaches `set -gx VIRTUAL_ENV`, and `--prompt` or `VIRTUALENV_PROMPT` reaches `set -gx VIRTUAL_ENV_PROMPT`. For `--prompt \"x\\'\\$(touch PWNED)'\\'\"`, the generated `activate.fish` contains:\n\n```fish\nset -gx VIRTUAL_ENV_PROMPT 'x\\'\"'\"'$(touch PWNED)'\"'\"'\\'\"'\"''\n```\n\n`source bin/activate.fish` runs `touch PWNED`, and fish prints no error. The same payload as a directory name runs through `VIRTUAL_ENV`. `--prompt .` takes the current directory's name, so running virtualenv inside a directory someone else named is enough. Without the backslash the payload runs nothing, and under bash it runs nothing either.\n\nvirtualenv generates `activate.fish` by default. An attacker needs to influence the environment path or prompt: a CI job or wrapper tool that passes a branch, project or job name through, or a directory in a cloned repository or unpacked archive.\n\nThe bug affects every release up to 21.14.1 that ships `activate.fish`. The GHSA-p58f-9548-mpm2 fix in 21.7.13 moved the placeholders outside their quotes but kept `shlex.quote`.\n\n### Patches\n\nFixed in 21.14.2. `activate.fish` now escapes backslashes and single quotes the way fish reads them, so the example above runs nothing and shows the prompt as given. Fix: https://github.com/pypa/virtualenv/commit/3bddeaf8d02c3ee7f576503efb082f8f494cfcab\n\n### Workarounds\n\nDo not create an environment whose path or prompt contains a backslash, and inspect `activate.fish` before you source one you did not create.\n\n### Credits\n\nKevin Stubbings (@Kwstubbs) of GitHub Security Lab reported this issue.","summary":"virtualenv's generated activate.fish script quotes the environment path and prompt with POSIX shlex rules, which fish reads differently, so a crafted path or prompt containing a backslash before a quote breaks out of the quoting. An attacker who controls a directory name or prompt value can get arbitrary shell commands run when a user sources activate.fish.","severity":"high","cvss":7.800000190734863,"epss":0.0021100000012665987,"exploited":false,"exploited_at":null,"products":[{"name":"virtualenv","fixed":["21.14.2"],"affected":"<= 21.14.1","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["ci","cwe-116","cwe-78","fish","package","python","virtualenv"],"not_affected_if":["bash-or-zsh-only","managed-hosting","no-fish-users"],"how_to_check":"Check the installed version with `pip show virtualenv` and treat anything at or below 21.14.1 as affected.\nFor environments created by you or your CI, grep activate.fish for `set -gx VIRTUAL_ENV` and `VIRTUAL_ENV_PROMPT` and look for unexpected $( ) content.","fix":"Upgrade virtualenv to 21.14.2 or later and recreate fish environments with it.\nUntil then, avoid backslashes in environment paths and prompts and inspect activate.fish before sourcing environments you did not create.","status":"open","published_at":"2026-10-20T22:19:30+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T19:52:54+00:00","updated_at":"2026-10-23T21:57:25+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-10-23T19:52:54Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade virtualenv to 21.14.2 or later and recreate fish environments with it.\nUntil then, avoid backslashes in environment paths and prompts and inspect activate.fish before sourcing environments you did not create.","summary":"virtualenv's generated activate.fish script quotes the environment path and prompt with POSIX shlex rules, which fish reads differently, so a crafted path or prompt containing a backslash before a quote breaks out of the quoting. An attacker who controls a directory name or prompt value can get arbitrary shell commands run when a user sources activate.fish.","category":"package","who_cares":["python","fish","virtualenv","ci"],"confidence":0.85,"analyst_note":"","how_to_check":"Check the installed version with `pip show virtualenv` and treat anything at or below 21.14.1 as affected.\nFor environments created by you or your CI, grep activate.fish for `set -gx VIRTUAL_ENV` and `VIRTUAL_ENV_PROMPT` and look for unexpected $( ) content.\nunknown","needs_analyst":false,"not_affected_if":["bash-or-zsh-only","no-fish-users","managed-hosting"],"severity_if_unknown":"high"},"cost_usd":0.000493,"input_tokens":2814,"output_tokens":424,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-113732':18A,26A '-116':30B '-2026':17A,25A '-78':32B '/pypa/virtualenv/commit/3bddeaf8d02c3ee7f576503efb082f8f494cfcab':355C '21.14.1':302C '21.14.2':327C '21.7.13':314C '3pg5':22A '9548':310C 'activate.fish':2A,40B,93B,191C,257C,305C,328C,371C 'affect':297C 'arbitrari':85B 'archiv':294C 'attack':74B,261C 'backslash':12A,64B,115C,136C,141C,243C,331C,368C 'bash':250C 'bin/activate.fish':203C 'branch':279C 'break':68B 'bug':296C 'c947':21A 'charact':122C 'ci':28B,271C 'clone':290C 'command':4A,87B,161C 'contain':62B,192C,366C 'control':76B 'craft':58B 'creat':359C,379C 'credit':380C 'current':226C 'cve':16A,24A 'cwe':29B,31B 'default':259C 'defin':96C 'differ':55B 'directori':78B,217C,227C,235C,287C 'either':254C 'els':237C 'end':146C,156C 'enough':240C 'env':172C,182C,197C,222C 'environ':44B,166C,266C,361C 'error':211C 'escap':130C,135C,330C 'everi':298C 'exampl':342C 'expand':159C 'fish':33B,53B,123C,158C,193C,208C,337C 'fishactiv':95C 'fix':312C,325C,352C 'follow':109C,164C 'function':108C 'generat':39B,190C,256C 'get':84B 'ghsa':20A,308C 'ghsa-c947-3pg5-gm8q':19A 'ghsa-p58f':307C 'github':385C 'github.com':354C 'github.com/pypa/virtualenv/commit/3bddeaf8d02c3ee7f576503efb082f8f494cfcab':353C 'given':351C 'gm8q':23A 'gx':170C,180C,195C 'impact':94C 'influenc':264C 'inherit':101C 'insid':116C,125C,233C 'inspect':370C 'issu':390C 'job':272C,282C 'kept':322C 'kevin':381C 'kwstubb':383C 'lab':387C 'liter':121C,148C 'move':315C 'mpm2':311C 'name':79B,218C,229C,238C,283C 'need':262C 'noth':247C,253C,345C 'one':375C 'outsid':318C 'p58f':309C 'packag':34B 'pass':277C 'patch':324C 'path':7A,45B,59B,167C,267C,363C 'payload':214C,245C 'placehold':317C 'posix':49B,110C 'print':209C 'project':280C 'prompt':9A,47B,61B,81B,174C,177C,183C,185C,198C,223C,269C,349C,365C 'put':154C 'pwned':188C,201C,206C 'python':35B 'quot':15A,42B,67B,72B,98C,118C,127C,131C,144C,320C,334C 'reach':168C,178C 'read':54B,124C,338C 'releas':299C 'report':388C 'repositori':291C 'return':104C 'rule':51B,112C 'run':3A,88B,204C,219C,231C,246C,252C,344C 'script':41B 'secur':386C 'set':169C,179C,194C 'sh':111C 'shell':86B 'ship':304C 'shlex':50B 'shlex.quote':105C,153C,323C 'show':347C 'singl':117C,126C,333C 'someon':236C 'somewher':149C 'sourc':92B,202C,374C 'stay':119C 'string':106C 'stub':382C 'substitut':162C 'take':224C 'therefor':145C 'tool':275C 'touch':187C,200C,205C 'unpack':293C 'user':91B 'valu':82B,138C 'viatemplateactivator.quote':102C 'virtual':171C,181C,196C,221C 'virtualenv':1A,27A,36B,37B,176C,232C,255C 'way':336C 'whose':362C 'without':241C 'workaround':356C 'wrapper':274C 'x':186C,199C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-20T22:19:30+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-c947-3pg5-gm8q","url":"https://github.com/advisories/GHSA-c947-3pg5-gm8q","severity":"high","published_at":"2026-10-21T16:39:58+00:00","modified_at":"2026-10-21T16:39:59+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-113732","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-113732","severity":null,"published_at":"2026-10-20T22:19:30+00:00","modified_at":"2026-10-22T16:47:38+00:00","first_seen_at":"2026-10-23T21:57:25+00:00","fetched_at":"2026-10-23T22:29:00+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}