feat: add mask_stdout to redact secrets from sandboxed process output#6
feat: add mask_stdout to redact secrets from sandboxed process output#6machado144 merged 7 commits intomainfrom
Conversation
Intercepts stdout/stderr from sandboxed processes at the Go layer and redacts secrets before they reach the terminal — a defense-in-depth layer on top of existing kernel-level sandbox protections. - MaskingWriter: line-buffered io.Writer with regex-based redaction - 5 built-in presets: openai, anthropic, aws_key, github, bearer - Custom patterns with show_prefix (partial reveal) and case_insensitive options - All presets + a generic key=value pattern enabled by default in aigate init - Banner shows active presets/patterns at sandbox startup - Zero behavioral change when mask_stdout is absent from config
… with releaseforge
StructLint — All checks passed73 rules validated against
|
There was a problem hiding this comment.
Core Changes
- Implemented
mask_stdoutfunctionality to redact sensitive information from sandboxed process output (stdout/stderr). - Introduced a
MaskingWriterservice that uses configurable presets and custom regex patterns for redaction. - Integrated output masking into the sandbox execution flow and updated configuration management to support
mask_stdout. - Enhanced CI/CD with PR title validation, AI code review, struct linting, and improved release automation using
releaseforge.
Concerns
None. The changes are additive, well-tested, and enhance the security posture of the application without introducing regressions or critical issues.
Verdict
Approve: The changes are well-implemented, thoroughly tested, and significantly improve the security and maintainability of the project. All internal breaking changes to interfaces have been correctly propagated and handled.
Code review performed by GEMINI - gemini-2.5-flash.
There was a problem hiding this comment.
Core Changes
- Implemented
mask_stdoutfeature to redact secrets from sandboxed process output using configurable presets and custom regex patterns. - Updated
PlatformandExecutorinterfaces to support customio.Writerfor stdout/stderr, allowing theMaskingWriterto intercept and redact output. - Enhanced CI/CD workflows with PR title validation, AI code review, struct linting, and updated release automation to use
releaseforgefor version bumping and release notes.
Concerns
None. The changes are well-implemented, and the new output masking feature adds a valuable defense-in-depth layer without introducing regressions or critical issues.
Verdict
Approve: The changes introduce a significant security enhancement and improve development workflows. All modifications appear correct and robust.
Code review performed by GEMINI - gemini-2.5-flash.
There was a problem hiding this comment.
Core Changes
- Implemented
mask_stdoutfunctionality to redact sensitive information from sandboxed process output using configurable presets and custom regex patterns. - Updated CI/CD workflows (
pr.yml,release.yml,test.yml) to include new linting tools (structlint,golangci-lint) and enforce conventional commit message formats for PR titles and commit messages. - Modified the
PlatformandExecutorinterfaces to support passingio.Writerforstdoutandstderr, allowing theMaskingWriterto intercept and redact output.
Verdict
Approve: The changes introduce a valuable security feature (output masking) and improve code quality through enhanced linting and commit standards. All breaking interface changes are handled internally, and the new functionality is well-tested. The release.yml input change is a functional update with clear intent.
Code review performed by GEMINI - gemini-2.5-flash.
When mask_stdout is configured, output writers are wrapped in a MaskingWriter (not *os.File). Go's exec package then connects the child process's stdout to a pipe, causing programs like claude to detect a non-TTY and refuse to start interactively (claude exits with "Input must be provided either through stdin or as a prompt argument when using --print"). Fix by routing through a PTY when stdout is wrapped and stdin is a terminal. The child process sees a real TTY on its stdout/stderr while the parent reads from the PTY master and applies masking before writing to the real terminal. Falls back to a plain pipe in non-interactive contexts (CI, piped input) so masking still works there. Also add aws_secret preset to mask AWS_SECRET_ACCESS_KEY env vars, which were not covered by the existing aws_key preset (AKIA... key IDs only). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Core Changes
- Implemented a new
mask_stdoutfeature to redact sensitive information from sandboxed process output using configurable presets and custom regex patterns. - Integrated PTY (pseudo-terminal) support for sandboxed commands when output masking is active, ensuring interactive AI tools function correctly while their output is filtered.
- Enhanced CI/CD pipelines and local development workflows with conventional commit validation, automated code review, structural linting, and improved release automation.
Concerns
⚠️ Theservices/platform_linux.gofile on line 157 has asetfaclcommand where the error is ignored (_ = p.exec.Run). While this is a cleanup step, silently failing to remove default ACL entries could lead to unexpected permissions if the initialsetfacl -xcommand also failed.
Verdict
Request Changes: The ignored error in services/platform_linux.go for setfacl is a potential issue that should be addressed. While it's a cleanup operation, silently failing to remove ACLs could leave unintended permissions on files/directories, which is a security concern for a sandbox tool. It should at least log the error or handle it more robustly.
Code review performed by GEMINI - gemini-2.5-flash.
- Add 4 tests for aws_secret preset: standard = format, YAML : format, no false-positive on AWS_ACCESS_KEY_ID, and both aws_key+aws_secret together covering a full .env file - Update TestBuiltinPresetNames to include aws_secret - Add O_CLOEXEC comment in pty.go explaining why the PTY master fd cannot be inherited by sandboxed processes (Go stdlib always opens with O_CLOEXEC per os/file_unix.go:279) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Core Changes
- Implemented
mask_stdoutfeature to redact sensitive information (API keys, tokens) from sandboxed process output using configurable presets and custom regex patterns. - Integrated output masking into the sandbox execution flow, including PTY (pseudo-terminal) handling for interactive commands to preserve TTY behavior while applying redaction.
- Updated CI/CD workflows (
pr.yml,release.yml,test.yml) andlefthookconfiguration to enforce conventional commits, run AI code reviews, and includegolangci-lintandstructlintchecks.
Concerns
None.
Verdict
Approve: The changes introduce a valuable security feature (output masking) with a robust implementation, including PTY support for interactive tools. The CI/CD and lefthook updates improve code quality and development practices. All identified interface changes are consistently applied across the codebase and tests. No critical issues, bugs, or security vulnerabilities were found.
Code review performed by GEMINI - gemini-2.5-flash.
Silently discarding the setfacl error for the default ACL removal (d:g:<group>) could leave inherited permissions on directories if the cleanup step fails, which is a security concern for a sandbox tool. Log it at warn level consistent with the access ACL removal above. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
machado144
left a comment
There was a problem hiding this comment.
Fixed in 0f62468. The setfacl -R -x d:g:<group> result is now checked and logged at warn level, consistent with the access ACL removal directly above it. Silently discarding it was the gap — if default ACL removal fails, the directory would still inherit deny entries for new files, which is exactly the kind of silent permission state a sandbox tool shouldn't leave behind.
There was a problem hiding this comment.
Core Changes
- Implemented a
mask_stdoutfeature to redact sensitive information from the standard output and error streams of sandboxed processes. This includes built-in presets for common API keys (e.g., OpenAI, AWS, GitHub) and support for custom regex patterns. - Updated CI/CD workflows to include PR title validation using Conventional Commits, AI-powered code reviews, and structural linting. The release workflow was also enhanced for automated version bumping and release note generation.
- Integrated
golangci-lintandstructlintinto localpre-commithooks to enforce code quality and project structure standards.
Concerns
None.
Verdict
Approve: The changes introduce valuable security enhancements (defense-in-depth for output masking) and significantly improve development and release workflows. The implementation appears robust, and all internal API changes are consistently applied across the codebase.
Code review performed by GEMINI - gemini-2.5-flash.
Summary
mask_stdoutconfig block that intercepts stdout/stderr from sandboxed processes and redacts secrets before they reach the terminalaigate initWhat's included
New:
services/masker.goMaskingWriter— line-bufferedio.Writerthat applies regex rules before forwarding to the real writer\n)Flush()handles the last line if it has no trailing newlineBuilt-in presets
openaisk-.../sk-proj-...sk-***anthropicsk-ant-...sk-ant-***aws_keyAKIA...AKIA***githubghp_,gho_,ghu_,ghs_,ghr_ghp_***bearerBearer <token>Bearer ***Custom pattern options
Default config (
aigate init) now ships with all 5 presets + one example pattern for generickey=valueassignments (api_key=,secret:,password=, etc.)Safety
mask_stdoutis absent from config,buildOutputWritersreturnsos.Stdout/os.Stderr— identical to previous behaviourRunPassthroughnow delegates toRunPassthroughWith(os.Stdout, os.Stderr, ...)— same resultRunSandboxed,Executor) are mechanical — all implementations and test mocks updatedgo vet+ race detector both passTest plan
go test ./... -count=1 -racepassesaigate initgenerates config withmask_stdoutpresets + example patternaigate run -- envwith an OpenAI key in env does not print the raw key[aigate] mask_stdout: ...line appears in startup banner when configured