#21861: fix: selective context gating for OWNER_ONLY privacy tags (#11900)
docs
agents
size: M
Cluster:
Error Handling Improvements
## Summary
Closes #11900 — **Context Leak: personal workspace files exposed to non-owner senders.**
- **Problem:** OpenClaw injected the full personal context (`USER.md`, `MEMORY.md`, etc.) into every session. Any stranger messaging your agent on a public channel could ask for — and receive — your private info (email, phone, home address, API keys, etc.).
- **Fix:** Introduced `<!-- OWNER_ONLY -->` / `<!-- /OWNER_ONLY -->` privacy tags. When a non-owner sends a message, `buildBootstrapContextFiles` dynamically replaces tagged regions with `[Content restricted to owner]` before the content reaches the LLM.
- **Safety:** Unclosed tags conservatively strip everything from the opening tag to end-of-file, preventing accidental leaks from malformed markup.
### What changed
| File | Change |
|------|--------|
| `src/agents/pi-embedded-helpers/bootstrap.ts` | Core stripping logic in `buildBootstrapContextFiles` — scans for OWNER_ONLY tags and redacts for non-owners |
| `src/agents/bootstrap-files.ts` | Threads `senderIsOwner` through to `buildBootstrapContextFiles`; removed redundant double-call to `filterBootstrapFilesForSession` |
| `src/agents/workspace.ts` | Cleaned up `filterBootstrapFilesForSession` signature (removed unused `senderIsOwner` param that belonged only in the build step) |
| `src/agents/pi-embedded-runner/run/attempt.ts` | Passes `senderIsOwner` from the runner into `resolveBootstrapContextForRun` |
| `src/hooks/bundled/bootstrap-extra-files/handler.ts` | Updated call signature to match new `filterBootstrapFilesForSession` params shape |
| `src/agents/pi-embedded-helpers/privacy-tags.test.ts` | **New** — 4 test cases: non-owner stripping, owner preservation, unclosed tag safety, multiple tags |
| `docs/reference/templates/AGENTS.md` | Added Privacy Gating rule to the Safety section |
| `docs/reference/templates/USER.md` | Added empty `<!-- OWNER_ONLY -->` block as a scaffold for users |
| `docs/concepts/agent-workspace.md` | Documented the tag usage for `USER.md` and `MEMORY.md` |
### How to use it
Just tell your agent:
> "Check my USER.md and MEMORY.md. If you see any private info like my email or phone, wrap them in the new `<!-- OWNER_ONLY -->` tags so they don't leak to others."
Or manually wrap sensitive sections:
```markdown
## About Me
- **Name:** Alice
- **Timezone:** PST
<!-- OWNER_ONLY -->
- **Email:** alice@example.com
- **Phone:** +1-555-0123
- **Home Address:** 123 Main St
<!-- /OWNER_ONLY -->
## Context
Works on the Lobster project...
```
When a non-owner messages your agent, they see the personality and public context, but the tagged sections are replaced with `[Content restricted to owner]`.
## Test plan
- [x] Unit tests pass: non-owner stripping, owner preservation, unclosed tag → strip to EOF, multiple tags in one file
- [x] Full test suite: 6437 passed (5 pre-existing Windows-only failures unrelated to this PR)
- [ ] Manual test: verify agent responds normally to owner with full context
- [ ] Manual test: verify non-owner sees redacted placeholders, cannot extract private info
## AI-assisted
This PR was developed with AI assistance. The code has been reviewed, tested, and is fully understood.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR implements privacy gating to prevent personal context leaks when non-owner users interact with agents on public channels. The implementation adds `<!-- OWNER_ONLY -->` HTML comment tags that can wrap sensitive information in workspace files like `USER.md` and `MEMORY.md`.
**Key Changes:**
- Core stripping logic in `buildBootstrapContextFiles` (`src/agents/pi-embedded-helpers/bootstrap.ts:212-230`) scans for OWNER_ONLY tags and replaces tagged regions with `[Content restricted to owner]` when `senderIsOwner` is false
- Unclosed tags conservatively strip content from opening tag to EOF to prevent accidental leaks
- `senderIsOwner` parameter threaded through the bootstrap pipeline from runner → bootstrap context resolution → file building
- Documentation updated with usage examples and safety guidelines
- Test coverage includes: non-owner stripping, owner preservation, unclosed tag handling, and multiple tags
**Unrelated Changes:**
The PR also includes an MS Teams SSRF security fix (commit `51837482`) that enforces allowlist checks on all redirect hops and prevents auth header forwarding to unauthorized destinations. This is a separate security improvement merged before the main privacy tag commit.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge with minor considerations - the privacy gating logic is sound and well-tested, though careful verification of the default behavior when senderIsOwner is undefined is recommended
- The implementation is solid with good test coverage and conservative fail-safe behavior (unclosed tags strip to EOF). The logic correctly handles the three states: owner (true), non-owner (false), and undefined. When undefined, the check `opts?.senderIsOwner === false` evaluates to false, meaning content is NOT stripped - this treats undefined as "owner" by default, which is the safe backward-compatible choice. The threading of senderIsOwner through the pipeline is clean and the MS Teams SSRF fix is a welcome security improvement. Score is 4 (not 5) only because the default undefined behavior should be explicitly verified in production to ensure it aligns with actual sender ownership detection.
- Pay close attention to `src/agents/pi-embedded-helpers/bootstrap.ts:204` - verify the default behavior when `senderIsOwner` is undefined aligns with your sender detection logic in production
<sub>Last reviewed commit: 810ef27</sub>
<!-- greptile_other_comments_section -->
<sub>(5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#8175: fix: suppress raw API errors for non-owners
by Rakshi2609 · 2026-02-03
77.4%
#10514: Security: harden AGENTS.md with gateway, prompt injection, and supp...
by catpilothq · 2026-02-06
75.3%
#8086: feat(security): Add prompt injection guard rail
by bobbythelobster · 2026-02-03
74.8%
#14222: core: add needsApproval to before_tool_call; move AgentShield to ex...
by Eventedge · 2026-02-11
74.8%
#9829: Fix MCP transport reconnect and SSE header handling
by mabengda · 2026-02-05
74.2%
#21055: security(cli): gate systemPromptReport behind --debug flag
by richvincent · 2026-02-19
74.0%
#5922: fix(security): add instruction confidentiality directive to system ...
by dan-redcupit · 2026-02-01
73.8%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
73.8%
#15756: [Security]: strip provider apiKey from models.json before prompt se...
by SecBear · 2026-02-13
73.8%
#17304: feat(gemini): robust handling for non-XML reasoning headers (`Think...
by YoshiaKefasu · 2026-02-15
73.7%