Environment variable pollution in vscode, codex extension sets DEBUG="release"
What version of the IDE extension are you using?
26.304.20706, vscode
What subscription do you have?
plus
Which IDE are you using?
vscode
What platform is your computer?
Microsoft Windows NT 10.0.26100.0 x64
What issue are you seeing?
Codex extension inject DEBUG=release env var into some other extension created integrated terminal. For now I can confirm, Kubernetes plugin created port-forward terminal have this pollution, which will then cause it turn on verbose logging.
❯ Get-ChildItem Env: | Sort-Object Name | rg DEBUG
DEBUG release
If disable codex extension, or just run the same command outside of vscode integrated terminal, no output.
And to verify:
❯ cd C:\Users\tpob\.vscode\extensions\openai.chatgpt-0.4.79\
❯ rg 'DEBUG.{0,100}release'
out\extension.js
36:`).some(n=>n.indexOf("(https.js:")!==-1||n.indexOf("node:https:")!==-1)}incrementSockets(e){if(this.maxSockets===1/0&&this.maxTotalSockets===1/0)return null;this.sockets[e]||(this.sockets[e]=[]);let r=new Uue.Socket({writable:!1});return this.sockets[e].push(r),this.totalSocketCount++,r}decrementSockets(e,r){if(!this.sockets[e]||r===null)return;let n=this.sockets[e],o=n.indexOf(r);o!==-1&&(n.splice(o,1),this.totalSocketCount--,n.length===0&&delete this.sockets[e])}getName(e){return(typeof e.secureEndpoint=="boolean"?e.secureEndpoint:this.isSecureEndpoint(e))?Fue.Agent.prototype.getName.call(this,e):super.getName(e)}createSocket(e,r,n){let o={...r,secureEndpoint:this.isSecureEndpoint(r)},i=this.getName(o),s=this.incrementSockets(i);Promise.resolve().then(()=>this.connect(e,o)).then(a=>{if(this.decrementSockets(i,s),a instanceof Dq.Agent)try{return a.addRequest(e,o)}catch(c){return n(c)}this[_o].currentSocket=a,super.createSocket(e,r,n)},a=>{this.decrementSockets(i,s),n(a)})}createConnection(){let e=this[_o].currentSocket;if(this[_o].currentSocket=void 0,!e)throw new Error("No socket was returned in the `connect()` function");return e}get defaultPort(){return this[_o].defaultPort??(this.protocol==="https:"?443:80)}set defaultPort(e){this[_o]&&(this[_o].defaultPort=e)}get protocol(){return this[_o].protocol??(this.isSecureEndpoint()?"https:":"http:")}set protocol(e){this[_o]&&(this[_o].protocol=e)}};nn.Agent=KR});var jq=S((jFe,zq)=>{var tu=1e3,ru=tu*60,nu=ru*60,Ys=nu*24,zue=Ys*7,jue=Ys*365.25;zq.exports=function(t,e){e=e||{};var r=typeof t;if(r==="string"&&t.length>0)return que(t);if(r==="number"&&isFinite(t))return e.long?Wue(t):Bue(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))};function que(t){if(t=String(t),!(t.length>100)){var e=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return r*jue;case"weeks":case"week":case"w":return r*zue;case"days":case"day":case"d":return r*Ys;case"hours":case"hour":case"hrs":case"hr":case"h":return r*nu;case"minutes":case"minute":case"mins":case"min":case"m":return r*ru;case"seconds":case"second":case"secs":case"sec":case"s":return r*tu;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function Bue(t){var e=Math.abs(t);return e>=Ys?Math.round(t/Ys)+"d":e>=nu?Math.round(t/nu)+"h":e>=ru?Math.round(t/ru)+"m":e>=tu?Math.round(t/tu)+"s":t+"ms"}function Wue(t){var e=Math.abs(t);return e>=Ys?pv(t,e,Ys,"day"):e>=nu?pv(t,e,nu,"hour"):e>=ru?pv(t,e,ru,"minute"):e>=tu?pv(t,e,tu,"second"):t+" ms"}function pv(t,e,r,n){var o=e>=r*1.5;return Math.round(t/r)+" "+n+(o?"s":"")}});var JR=S((qFe,qq)=>{function Gue(t){r.debug=r,r.default=r,r.coerce=c,r.disable=s,r.enable=o,r.enabled=a,r.humanize=jq(),r.destroy=u,Object.keys(t).forEach(l=>{r[l]=t[l]}),r.names=[],r.skips=[],r.formatters={};function e(l){let d=0;for(let f=0;f<l.length;f++)d=(d<<5)-d+l.charCodeAt(f),d|=0;return r.colors[Math.abs(d)%r.colors.length]}r.selectColor=e;function r(l){let d,f=null,m,g;function v(...w){if(!v.enabled)return;let _=v,k=Number(new Date),E=k-(d||k);_.diff=E,_.prev=d,_.curr=k,d=k,w[0]=r.coerce(w[0]),typeof w[0]!="string"&&w.unshift("%O");let T=0;w[0]=w[0].replace(/%([a-zA-Z%])/g,(G,Q)=>{if(G==="%%")return"%";T++;let Pe=r.formatters[Q];if(typeof Pe=="function"){let ie=w[T];G=Pe.call(_,ie),w.splice(T,1),T--}return G}),r.formatArgs.call(_,w),(_.log||r.log).apply(_,w)}return v.namespace=l,v.useColors=r.useColors(),v.color=r.selectColor(l),v.extend=n,v.destroy=r.destroy,Object.defineProperty(v,"enabled",{enumerable:!0,configurable:!1,get:()=>f!==null?f:(m!==r.namespaces&&(m=r.namespaces,g=r.enabled(l)),g),set:w=>{f=w}}),typeof r.init=="function"&&r.init(v),v}function n(l,d){let f=r(this.namespace+(typeof d>"u"?":":d)+l);return f.log=this.log,f}function o(l){r.save(l),r.namespaces=l,r.names=[],r.skips=[];let d=(typeof l=="string"?l:"").trim().replace(/\s+/g,",").split(",").filter(Boolean);for(let f of d)f[0]==="-"?r.skips.push(f.slice(1)):r.names.push(f)}function i(l,d){let f=0,m=0,g=-1,v=0;for(;f<l.length;)if(m<d.length&&(d[m]===l[f]||d[m]==="*"))d[m]==="*"?(g=m,v=f,m++):(f++,m++);else if(g!==-1)m=g+1,v++,f=v;else return!1;for(;m<d.length&&d[m]==="*";)m++;return m===d.length}function s(){let l=[...r.names,...r.skips.map(d=>"-"+d)].join(",");return r.enable(""),l}function a(l){for(let d of r.skips)if(i(l,d))return!1;for(let d of r.names)if(i(l,d))return!0;return!1}function c(l){return l instanceof Error?l.stack||l.message:l}function u(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return r.enable(r.load()),r}qq.exports=Gue});var Bq=S((jr,fv)=>{jr.formatArgs=Vue;jr.save=Zue;jr.load=Kue;jr.useColors=Hue;jr.storage=Jue();jr.destroy=(()=>{let t=!1;return()=>{t||(t=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();jr.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function Hue(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let t;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(t=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(t[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function Vue(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+fv.exports.humanize(this.diff),!this.useColors)return;let e="color: "+this.color;t.splice(1,0,e,"color: inherit");let r=0,n=0;t[0].replace(/%[a-zA-Z%]/g,o=>{o!=="%%"&&(r++,o==="%c"&&(n=r))}),t.splice(n,0,e)}jr.log=console.debug||console.log||(()=>{});function Zue(t){try{t?jr.storage.setItem("debug",t):jr.storage.removeItem("debug")}catch{}}function Kue(){let t;try{t=jr.storage.getItem("debug")||jr.storage.getItem("DEBUG")}catch{}return!t&&typeof process<"u"&&"env"in process&&(t="release"),t}function Jue(){try{return localStorage}catch{}}fv.exports=JR()(jr);var{formatters:Yue}=fv.exports;Yue.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}});var Gq=S((BFe,Wq)=>{"use strict";Wq.exports=(t,e=process.argv)=>{let r=t.startsWith("-")?"":t.length===1?"-":"--",n=e.indexOf(r+t),o=e.indexOf("--");return n!==-1&&(o===-1||n<o)}});var Zq=S((WFe,Vq)=>{"use strict";var Xue=require("os"),Hq=require("tty"),bn=Gq(),{env:tr}=process,mv;bn("no-color")||bn("no-colors")||bn("color=false")||bn("color=never")?mv=0:(bn("color")||bn("colors")||bn("color=true")||bn("color=always"))&&(mv=1);function Que(){if("FORCE_COLOR"in tr)return tr.FORCE_COLOR==="true"?1:tr.FORCE_COLOR==="false"?0:tr.FORCE_COLOR.length===0?1:Math.min(Number.parseInt(tr.FORCE_COLOR,10),3)}function ele(t){return t===0?!1:{level:t,hasBasic:!0,has256:t>=2,has16m:t>=3}}function tle(t,{streamIsTTY:e,sniffFlags:r=!0}={}){let n=Que();n!==void 0&&(mv=n);let o=r?mv:n;if(o===0)return 0;if(r){if(bn("color=16m")||bn("color=full")||bn("color=truecolor"))return 3;if(bn("color=256"))return 2}if(t&&!e&&o===void 0)return 0;let i=o||0;if(tr.TERM==="dumb")return i;if(process.platform==="win32"){let s=Xue.release().split(".");return Number(s[0])>=10&&Number(s[2])>=10586?Number(s[2])>=14931?3:2:1}if("CI"in tr)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE","DRONE"].some(s=>s in tr)||tr.CI_NAME==="codeship"?1:i;if("TEAMCITY_VERSION"in tr)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(tr.TEAMCITY_VERSION)?1:0;if(tr.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in tr){let s=Number.parseInt((tr.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(tr.TERM_PROGRAM){case"iTerm.app":return s>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(tr.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(tr.TERM)||"COLORTERM"in tr?1:i}function YR(t,e={}){let r=tle(t,{streamIsTTY:t&&t.isTTY,...e});return ele(r)}Vq.exports={supportsColor:YR,stdout:YR({isTTY:Hq.isatty(1)}),stderr:YR({isTTY:Hq.isatty(2)})}});var Jq=S((rr,gv)=>{var rle=require("tty"),hv=require("util");rr.init=ule;rr.log=sle;rr.formatArgs=ole;rr.save=ale;rr.load=cle;rr.useColors=nle;rr.destroy=hv.deprecate(()=>{},"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");rr.colors=[6,2,3,4,5,1];try{let t=Zq();t&&(t.stderr||t).level>=2&&(rr.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch{}rr.inspectOpts=Object.keys(process.env).filter(t=>/^debug_/i.test(t)).reduce((t,e)=>{let r=e.substring(6).toLowerCase().replace(/_([a-z])/g,(o,i)=>i.toUpperCase()),n=process.env[e];return/^(yes|on|true|enabled)$/i.test(n)?n=!0:/^(no|off|false|disabled)$/i.test(n)?n=!1:n==="null"?n=null:n=Number(n),t[r]=n,t},{});function nle(){return"colors"in rr.inspectOpts?!!rr.inspectOpts.colors:rle.isatty(process.stderr.fd)}function ole(t){let{namespace:e,useColors:r}=this;if(r){let n=this.color,o="\x1B[3"+(n<8?n:"8;5;"+n),i=` ${o};1m${e} \x1B[0m`;t[0]=i+t[0].split(`
39:`)}function ale(t){t?process.env.DEBUG=t:delete"release"}function cle(){return"release"}function ule(t){t.inspectOpts={};let e=Object.keys(rr.inspectOpts);for(let r=0;r<e.length;r++)t.inspectOpts[e[r]]=rr.inspectOpts[e[r]]}gv.exports=JR()(rr);var{formatters:Kq}=gv.exports;Kq.o=function(t){return this.inspectOpts.colors=this.useColors,hv.inspect(t,this.inspectOpts).split(`
which means the packed extension indeed have this DEBUG env var setting logic. But since source code of it not available, I can't see which kind of integrated terminal it affects.
What steps can reproduce the bug?
- install codex extension
- install kubernetes extension
ms-kubernetes-tools.vscode-kubernetes-tools. - right click a pod and select port-forward, to forward an arbitary port. (fig 1) thiis will open an integrated terminal.
- in the created terminal,
ctrl + cto interrupt the process, and check the env var like above - turn off codex extension and try again
<img width="703" height="1037" alt="Image" src="https://github.com/user-attachments/assets/61e82edc-5313-4b94-beba-307aa3e81c54" />
but i think from the packed extension it may be more easy to reproduce it because finding a set k8s environment seems a little bit heavy for debugging this problem.
What is the expected behavior?
no env var pollution
Additional information
_No response_
8 Comments
I have also found this issue in vscode in macos. Its breaking my tests that relies on DEBUG being a bool variable.
I'm seeing the same issue. I have code that relies on the
DEBUGvariable being a bool, which breaks when running python test discovery.To reproduce, create a python file
test_dummy.py:Then run VSCode test discovery with the Codex extension enabled. This is the resulting log:
With Codex disabled,
DEBUGis not set.I'm having the same issue with some Rails app, and Codex was triggering lots of tests to fail. Codex admitted it's his fault, but he can't pinpoint to exact source of this.
I traced the exact source of the
DEBUG=releasepollution by inspecting the bundledout/extension.js.The culprit is this code:
This appears to be a bundled/minified version of the popular
debugnpm library. The original source likely looked something like:During bundling/minification, the build mode constant
"release"was inlined in the wrong place: thedelete process.env.DEBUGexpression becamedelete "release", which is a no-op in JavaScript (deleting a string literal does nothing). Meanwhile,KAe()always returns the hardcoded string"release".The result is that at extension initialization,
VAe("release")is called →process.env.DEBUG = "release"is set, and the cleanup branch (delete process.env.DEBUG) is permanently broken by the minifier substituting the variable with its constant value.This
DEBUG=releasethen propagates to all child processes spawned by the extension's sandbox, affecting any framework or tool that readsDEBUGfrom the environment (Django, Spring Boot, etc.) with unexpected results.Root cause: minifier/tree-shaker constant folding incorrectly replaced a variable reference with its string value inside a
deleteexpression.Fix: ensure the
delete process.env.DEBUGpath is preserved correctly in the bundle, or explicitlyunset/delete process.env.DEBUGafter the extension initializes thedebuglibrary.woa! This is a nice investigation here.
I hope it will be fixed though. I'm not sure how these issues get into work, if ever.
as a workaround you can change:
{t?process.env.DEBUG=t:delete"release"}with:
{t?process.env.DEBUG=t:delete process.env.DEBUG}and
{return"release"}with:
{return process.env.DEBUG}in
out/extension.jsplease fix this bug, I've already got multiple updates, I don't want to fix the obfuscated js each update
I can confirm this is still reproducible on Windows with Codex VS Code extension versions 26.623.141536 and 26.707.41301, and it has a concrete downstream impact on Spring Boot applications launched through Gradle.
Observed inheritance chain:
Even with the application root logging level set to INFO, Spring Boot interprets the inherited
DEBUGenvironment variable as its globaldebugproperty. The result is framework DEBUG output and the full conditions evaluation report.I ran a controlled A/B test using the same application source, configuration, database, and Spring Boot 3.5.6 runtime:
DEBUG=release: framework DEBUG lines and the conditions evaluation report appearedDEBUGremoved immediately beforebootRun: application reached Started with 0 DEBUG lines and no conditions evaluation reportThis also rules out an application logger being changed later at runtime: the debug output is already active during
SpringApplicationbootstrap.The affected extension bundles contain behavior equivalent to:
The debug package initialization then enables the value returned by
load(), leavingprocess.env.DEBUG=releasein the shared extension host.Temporary PowerShell workaround:
A safe downstream mitigation is to remove case-insensitive
DEBUGentries only from each Spring BootBootRuntask's child environment. That avoids changing the developer's global environment or unrelated Gradle tasks.