Skip to content

feat: add mask_stdout to redact secrets from sandboxed process output#6

Merged
machado144 merged 7 commits intomainfrom
feat/mask-stdout
Mar 12, 2026
Merged

feat: add mask_stdout to redact secrets from sandboxed process output#6
machado144 merged 7 commits intomainfrom
feat/mask-stdout

Conversation

@machado144
Copy link
Contributor

Summary

  • Adds a mask_stdout config block that intercepts stdout/stderr from sandboxed processes and redacts secrets before they reach the terminal
  • Defense-in-depth layer on top of existing kernel-level protections (namespaces, ACLs, iptables) — purely additive, zero impact on existing sandbox behaviour
  • 5 built-in presets covering the most common secret formats, all enabled by default via aigate init

What's included

New: services/masker.go

  • MaskingWriter — line-buffered io.Writer that applies regex rules before forwarding to the real writer
  • Secrets split across write chunks on the same line are still caught (buffered until \n)
  • Flush() handles the last line if it has no trailing newline

Built-in presets

Preset Matches Output
openai sk-... / sk-proj-... sk-***
anthropic sk-ant-... sk-ant-***
aws_key AKIA... AKIA***
github ghp_, gho_, ghu_, ghs_, ghr_ ghp_***
bearer Bearer <token> Bearer ***

Custom pattern options

mask_stdout:
  patterns:
    - regex: "myapp-secret-[a-z0-9]+"
      show_prefix: 0          # fully masked
    - regex: "token-[a-zA-Z0-9]{16}"
      show_prefix: 6          # token-***
    - regex: "(?:password|secret)\\s*[=:]\\s*\\S+"
      show_prefix: 0
      case_insensitive: true  # catches PASSWORD=, Password=, etc.

Default config (aigate init) now ships with all 5 presets + one example pattern for generic key=value assignments (api_key=, secret:, password=, etc.)

Safety

  • When mask_stdout is absent from config, buildOutputWriters returns os.Stdout/os.Stderr — identical to previous behaviour
  • RunPassthrough now delegates to RunPassthroughWith(os.Stdout, os.Stderr, ...) — same result
  • All platform interface changes (RunSandboxed, Executor) are mechanical — all implementations and test mocks updated
  • No new external dependencies
  • go vet + race detector both pass

Test plan

  • go test ./... -count=1 -race passes
  • aigate init generates config with mask_stdout presets + example pattern
  • aigate run -- env with an OpenAI key in env does not print the raw key
  • Existing sandbox restrictions (deny_read, deny_exec, allow_net) unaffected
  • [aigate] mask_stdout: ... line appears in startup banner when configured

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
@github-actions
Copy link

github-actions bot commented Mar 10, 2026

StructLint — All checks passed

73 rules validated against .structlint.yaml. No violations found.

View full run · Powered by StructLint

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core Changes

  • Implemented mask_stdout functionality to redact sensitive information from sandboxed process output (stdout/stderr).
  • Introduced a MaskingWriter service 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.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core Changes

  • Implemented mask_stdout feature to redact secrets from sandboxed process output using configurable presets and custom regex patterns.
  • Updated Platform and Executor interfaces to support custom io.Writer for stdout/stderr, allowing the MaskingWriter to intercept and redact output.
  • Enhanced CI/CD workflows with PR title validation, AI code review, struct linting, and updated release automation to use releaseforge for 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.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core Changes

  • Implemented mask_stdout functionality 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 Platform and Executor interfaces to support passing io.Writer for stdout and stderr, allowing the MaskingWriter to 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.

@murilopmachado murilopmachado self-assigned this Mar 11, 2026
@murilopmachado murilopmachado self-requested a review March 11, 2026 00:55
Copy link
Collaborator

@murilopmachado murilopmachado left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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>
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core Changes

  • Implemented a new mask_stdout feature 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

  • ⚠️ The services/platform_linux.go file on line 157 has a setfacl command 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 initial setfacl -x command 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>
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core Changes

  • Implemented mask_stdout feature 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) and lefthook configuration to enforce conventional commits, run AI code reviews, and include golangci-lint and structlint checks.

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>
Copy link
Contributor Author

@machado144 machado144 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core Changes

  • Implemented a mask_stdout feature 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-lint and structlint into local pre-commit hooks 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.

@machado144 machado144 merged commit 1773e73 into main Mar 12, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants