{"id":"GHSA-4mqq-99j3-3hmv","kind":"vulnerability","aliases":["GHSA-4mqq-99j3-3hmv"],"related":[],"title":"AI SDK Harness session resume can expose host credentials to sandboxed code","description":"### Summary\r\nA vulnerability in AI SDK Harness credential brokering can expose host credentials to sandboxed code when an existing session is resumed with a newly introduced credential environment variable.\r\n\r\nCredential brokering normally supplies non-secret placeholders to the sandbox and substitutes real credentials outside the sandbox boundary for approved outbound requests. Affected adapters reuse the credential-placeholder map saved with the previous session without ensuring that it covers the current authentication environment. As a result, newly introduced credentials can be passed into the sandbox without being replaced by placeholders.\r\n\r\n### Impact\r\n\r\nAn attacker who can execute code or influence agent activity inside an affected resumed sandbox may read and exfiltrate exposed credentials without escaping the sandbox.\r\n\r\nDepending on the adapter and authentication configuration, exposed credentials may include model-provider API keys, source-control tokens, or OAuth tokens. Subsequent access depends on the permissions and scopes granted to those credentials and may include access to private data, unauthorized changes, or paid API usage.\r\n\r\nExploitation requires all of the following:\r\n\r\n- An affected Harness adapter uses credential brokering with a network sandbox supporting request transformations.\r\n- The application resumes a session using previously saved lifecycle state.\r\n- The resumed session introduces a credential variable absent from the saved placeholder map.\r\n- An attacker can cause code or agent activity inside the resumed sandbox to access the exposed credential.\r\n\r\nFor example, changing providers or authentication modes between session creation and resume can introduce a new credential variable.\r\n\r\nFresh sessions and credential rotations that retain the same credential variable names were not affected in the tested scenarios.\r\n\r\n### Patches\r\nA fix is proposed in https://github.com/vercel/ai/pull/21599.\r\n\r\nThe fix reconciles the sandbox credential environment with the current authentication environment on every start and resume. It preserves placeholders for matching credential names, generates placeholders for newly introduced credentials, and removes entries for credentials no longer present.\r\n\r\n### Workarounds\r\nUntil patched versions are available, create a fresh session and sandbox whenever the credential variable names change. Do not reuse saved lifecycle state across those transitions.\r\n\r\nIf an affected session may have exposed credentials to untrusted sandbox activity, revoke or rotate those credentials and review their usage.\r\n\r\n### Credits\r\nReported by 0x4ym3n through HackerOne.","summary":"When an AI SDK Harness session is resumed with a credential environment variable that was not in the saved session, that credential is passed into the sandbox in raw form instead of as a placeholder. Code or agent activity inside the sandbox can then read and exfiltrate the credential, such as a model-provider API key, source-control token or OAuth token.","severity":"high","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@ai-sdk/harness-opencode","fixed":["1.0.135"],"affected":"< 1.0.135","ecosystem":"npm"},{"name":"@ai-sdk/harness-claude-code","fixed":["1.0.137"],"affected":"< 1.0.137","ecosystem":"npm"},{"name":"@ai-sdk/harness-codex","fixed":["1.0.135"],"affected":"< 1.0.135","ecosystem":"npm"},{"name":"@ai-sdk/harness-deepagents","fixed":["1.0.133"],"affected":"< 1.0.133","ecosystem":"npm"},{"name":"@ai-sdk/harness","fixed":["1.0.133"],"affected":"< 1.0.133","ecosystem":"npm"},{"name":"@ai-sdk/harness-acp","fixed":["1.0.71"],"affected":"< 1.0.71","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["agents","ai-sdk","ai-tooling","cwe-200","npm","sandbox"],"not_affected_if":["fresh-sessions-only","no-session-resume","same-credential-names"],"how_to_check":"- Check the installed version of each `@ai-sdk/harness*` package in your lockfile against the fixed versions listed in the sources.\n- Look for code that resumes sessions from saved lifecycle state while switching providers or authentication modes between creation and resume.","fix":"- `@ai-sdk/harness-opencode` to `1.0.135` or later\n- `@ai-sdk/harness-claude-code` to `1.0.137` or later\n- `@ai-sdk/harness-codex` to `1.0.135` or later\n- `@ai-sdk/harness-deepagents` and `@ai-sdk/harness` to `1.0.133` or later\n- `@ai-sdk/harness-acp` to `1.0.71` or later","status":"open","published_at":"2026-09-28T17:44:55+00:00","first_seen_at":"2026-11-09T22:53:18+00:00","triaged_at":"2026-11-09T23:01:23+00:00","updated_at":"2026-11-09T23:01:23+00:00","category":"ai-tooling","confidence":0.85,"triage":{"at":"2026-11-09T23:01:23Z","model":"claude-haiku-5-5","output":{"fix":"- `@ai-sdk/harness-opencode` to `1.0.135` or later\n- `@ai-sdk/harness-claude-code` to `1.0.137` or later\n- `@ai-sdk/harness-codex` to `1.0.135` or later\n- `@ai-sdk/harness-deepagents` and `@ai-sdk/harness` to `1.0.133` or later\n- `@ai-sdk/harness-acp` to `1.0.71` or later","reach":"unknown","title":"AI SDK Harness: resumed sessions can expose host credentials to sandboxed code","impact":"high","summary":"When an AI SDK Harness session is resumed with a credential environment variable that was not in the saved session, that credential is passed into the sandbox in raw form instead of as a placeholder. Code or agent activity inside the sandbox can then read and exfiltrate the credential, such as a model-provider API key, source-control token or OAuth token.","audience":"developers","category":"ai-tooling","nicknames":[],"who_cares":["ai-sdk","agents","sandbox","npm"],"confidence":0.85,"fix_status":"available","needs_auth":"unknown","worst_case":"Model API keys and source-control tokens readable by sandboxed code","analyst_note":"","how_to_check":"- Check the installed version of each `@ai-sdk/harness*` package in your lockfile against the fixed versions listed in the sources.\n- Look for code that resumes sessions from saved lifecycle state while switching providers or authentication modes between creation and resume.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["fresh-sessions-only","no-session-resume","same-credential-names"],"who_is_affected":"Applications that use an affected AI SDK Harness adapter with credential brokering on a network sandbox that supports request transformations, and that resume sessions from previously saved lifecycle state. Exposure requires a credential variable that was absent from the saved session, for example after changing providers or authentication modes. Fresh sessions and credential rotations that keep the same variable names were not affected in the tested scenarios.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000924,"input_tokens":5479,"output_tokens":752,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-200':55B '/harness':42A '/harness-acp':46A '/harness-claude-code':30A '/harness-codex':34A '/harness-deepagents':38A '/harness-opencode':26A '/vercel/ai/pull/21599.':392C '0x4ym3n':482C '3hmv':17A,22A '4mqq':15A,20A '99j3':16A,21A 'absent':324C 'access':263C,277C,343C 'across':455C 'activ':96B,223C,337C,469C 'adapt':175C,242C,296C 'affect':174C,226C,294C,379C,460C 'agent':47B,95B,222C,336C 'ai':1A,24A,28A,32A,36A,40A,44A,49B,52B,60B,126C 'ai-sdk':23A,27A,31A,35A,39A,43A,48B 'ai-tool':51B 'api':113B,253C,285C 'applic':308C 'approv':171C 'attack':215C,331C 'authent':194C,244C,352C,403C 'avail':436C 'boundari':169C 'broker':130C,152C,299C 'caus':333C 'chang':282C,349C,448C 'code':12A,93B,137C,219C,334C 'configur':245C 'control':117B,257C 'cover':191C 'creat':437C 'creation':356C 'credenti':9A,68B,79B,106B,129C,134C,148C,151C,165C,179C,201C,234C,247C,273C,298C,322C,346C,363C,368C,374C,398C,415C,422C,427C,445C,465C,474C 'credential-placehold':178C 'credit':479C 'current':193C,402C 'cwe':54B 'data':280C 'depend':239C,264C 'ensur':188C 'entri':425C 'environ':69B,149C,195C,399C,404C 'escap':236C 'everi':406C 'exampl':348C 'execut':218C 'exfiltr':104B,232C 'exist':140C 'exploit':287C 'expos':7A,132C,233C,246C,345C,464C 'fix':386C,394C 'follow':292C 'form':87B 'fresh':365C,439C 'generat':417C 'ghsa':14A,19A 'ghsa-4mqq-99j3-3hmv':13A,18A 'github.com':391C 'github.com/vercel/ai/pull/21599.':390C 'grant':270C 'hackeron':484C 'har':3A,62B,128C,295C 'host':8A,133C 'impact':213C 'includ':249C,276C 'influenc':221C 'insid':97B,224C,338C 'instead':88B 'introduc':147C,200C,320C,360C,421C 'key':114B,254C 'lifecycl':315C,453C 'longer':429C 'map':181C,329C 'match':414C 'may':229C,248C,275C,462C 'mode':353C 'model':111B,251C 'model-provid':110B,250C 'name':376C,416C,447C 'network':302C 'new':362C 'newli':146C,199C,420C 'non':156C 'non-secret':155C 'normal':153C 'npm':56B 'oauth':120B,260C 'outbound':172C 'outsid':166C 'paid':284C 'pass':81B,204C 'patch':384C,433C 'permiss':267C 'placehold':92B,158C,180C,212C,328C,412C,418C 'present':430C 'preserv':411C 'previous':185C,313C 'privat':279C 'propos':388C 'provid':112B,252C,350C 'raw':86B 'read':102B,230C 'real':164C 'reconcil':395C 'remov':424C 'replac':210C 'report':480C 'request':173C,305C 'requir':288C 'result':198C 'resum':5A,65B,143C,227C,309C,318C,340C,358C,409C 'retain':371C 'reus':176C,451C 'review':476C 'revok':470C 'rotat':369C,472C 'sandbox':11A,57B,84B,99B,136C,161C,168C,207C,228C,238C,303C,341C,397C,442C,468C 'save':76B,182C,314C,327C,452C 'scenario':383C 'scope':269C 'sdk':2A,25A,29A,33A,37A,41A,45A,50B,61B,127C 'secret':157C 'session':4A,63B,77B,141C,186C,311C,319C,355C,366C,440C,461C 'sourc':116B,256C 'source-control':115B,255C 'start':407C 'state':316C,454C 'subsequ':262C 'substitut':163C 'summari':122C 'suppli':154C 'support':304C 'test':382C 'token':118B,121B,258C,261C 'tool':53B 'transform':306C 'transit':457C 'unauthor':281C 'untrust':467C 'usag':286C,478C 'use':297C,312C 'variabl':70B,150C,323C,364C,375C,446C 'version':434C 'vulner':124C 'whenev':443C 'without':187C,208C,235C 'workaround':431C","nicknames":[],"mitigations":[],"who_affected":"Applications that use an affected AI SDK Harness adapter with credential brokering on a network sandbox that supports request transformations, and that resume sessions from previously saved lifecycle state. Exposure requires a credential variable that was absent from the saved session, for example after changing providers or authentication modes. Fresh sessions and credential rotations that keep the same variable names were not affected in the tested scenarios.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["harness","harnessacp","harnessclaudecode","harnesscodex","harnessdeepagents","harnessopencode"],"ptokens":["acp","ai","claude","code","codex","deepagents","harness","opencode","sdk"],"labels":["agents","aisdk","aitooling","cwe200","high","npm","sandbox"],"campaign_type":null,"search_a":"'/harness':42A '/harness-acp':46A '/harness-claude-code':30A '/harness-codex':34A '/harness-deepagents':38A '/harness-opencode':26A '3hmv':17A,22A '4mqq':15A,20A '99j3':16A,21A 'ai':1A,24A,28A,32A,36A,40A,44A 'ai-sdk':23A,27A,31A,35A,39A,43A 'code':12A 'credenti':9A 'expos':7A 'ghsa':14A,19A 'ghsa-4mqq-99j3-3hmv':13A,18A 'har':3A 'host':8A 'resum':5A 'sandbox':11A 'sdk':2A,25A,29A,33A,37A,41A,45A 'session':4A","search_b":"'-200':55B 'activ':96B 'agent':47B,95B 'ai':49B,52B,60B 'ai-sdk':48B 'ai-tool':51B 'api':113B 'code':93B 'control':117B 'credenti':68B,79B,106B 'cwe':54B 'environ':69B 'exfiltr':104B 'form':87B 'har':62B 'insid':97B 'instead':88B 'key':114B 'model':111B 'model-provid':110B 'npm':56B 'oauth':120B 'pass':81B 'placehold':92B 'provid':112B 'raw':86B 'read':102B 'resum':65B 'sandbox':57B,84B,99B 'save':76B 'sdk':50B,61B 'session':63B,77B 'sourc':116B 'source-control':115B 'token':118B,121B 'tool':53B 'variabl':70B","search_c":"'/vercel/ai/pull/21599.':392C '0x4ym3n':482C 'absent':324C 'access':263C,277C,343C 'across':455C 'activ':223C,337C,469C 'adapt':175C,242C,296C 'affect':174C,226C,294C,379C,460C 'agent':222C,336C 'ai':126C 'api':253C,285C 'applic':308C 'approv':171C 'attack':215C,331C 'authent':194C,244C,352C,403C 'avail':436C 'boundari':169C 'broker':130C,152C,299C 'caus':333C 'chang':282C,349C,448C 'code':137C,219C,334C 'configur':245C 'control':257C 'cover':191C 'creat':437C 'creation':356C 'credenti':129C,134C,148C,151C,165C,179C,201C,234C,247C,273C,298C,322C,346C,363C,368C,374C,398C,415C,422C,427C,445C,465C,474C 'credential-placehold':178C 'credit':479C 'current':193C,402C 'data':280C 'depend':239C,264C 'ensur':188C 'entri':425C 'environ':149C,195C,399C,404C 'escap':236C 'everi':406C 'exampl':348C 'execut':218C 'exfiltr':232C 'exist':140C 'exploit':287C 'expos':132C,233C,246C,345C,464C 'fix':386C,394C 'follow':292C 'fresh':365C,439C 'generat':417C 'github.com':391C 'github.com/vercel/ai/pull/21599.':390C 'grant':270C 'hackeron':484C 'har':128C,295C 'host':133C 'impact':213C 'includ':249C,276C 'influenc':221C 'insid':224C,338C 'introduc':147C,200C,320C,360C,421C 'key':254C 'lifecycl':315C,453C 'longer':429C 'map':181C,329C 'match':414C 'may':229C,248C,275C,462C 'mode':353C 'model':251C 'model-provid':250C 'name':376C,416C,447C 'network':302C 'new':362C 'newli':146C,199C,420C 'non':156C 'non-secret':155C 'normal':153C 'oauth':260C 'outbound':172C 'outsid':166C 'paid':284C 'pass':204C 'patch':384C,433C 'permiss':267C 'placehold':158C,180C,212C,328C,412C,418C 'present':430C 'preserv':411C 'previous':185C,313C 'privat':279C 'propos':388C 'provid':252C,350C 'read':230C 'real':164C 'reconcil':395C 'remov':424C 'replac':210C 'report':480C 'request':173C,305C 'requir':288C 'result':198C 'resum':143C,227C,309C,318C,340C,358C,409C 'retain':371C 'reus':176C,451C 'review':476C 'revok':470C 'rotat':369C,472C 'sandbox':136C,161C,168C,207C,228C,238C,303C,341C,397C,442C,468C 'save':182C,314C,327C,452C 'scenario':383C 'scope':269C 'sdk':127C 'secret':157C 'session':141C,186C,311C,319C,355C,366C,440C,461C 'sourc':256C 'source-control':255C 'start':407C 'state':316C,454C 'subsequ':262C 'substitut':163C 'summari':122C 'suppli':154C 'support':304C 'test':382C 'token':258C,261C 'transform':306C 'transit':457C 'unauthor':281C 'untrust':467C 'usag':286C,478C 'use':297C,312C 'variabl':150C,323C,364C,375C,446C 'version':434C 'vulner':124C 'whenev':443C 'without':187C,208C,235C 'workaround':431C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-09-28T17:44:55+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-4mqq-99j3-3hmv","url":"https://github.com/vercel/ai/security/advisories/GHSA-4mqq-99j3-3hmv","severity":"high","published_at":"2026-09-28T17:44:55+00:00","modified_at":"2026-09-28T17:44:55+00:00","first_seen_at":"2026-11-09T22:53:18+00:00","fetched_at":"2026-11-09T22:53:18+00:00","note":null}],"documents":[{"id":110473,"url":"https://github.com/vercel/ai/security/advisories/GHSA-4mqq-99j3-3hmv","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}