pdftract/notes/bf-688te.md
jedarden 2ba56d8186 feat(bf-3vo80): add UnmappedGlyphNamesConfig struct to build.rs
Added typed config struct for unmapped glyph names configuration:
- unmapped_glyph_names: Vec<String> - list of glyph names to skip
- description: Option<String> - optional documentation
- version: Option<String> - format version identifier

The struct represents the JSON structure from build/unmapped-glyph-names.json
and is ready for parsing implementation in follow-up beads.

Closes bf-3vo80. Verification: notes/bf-3vo80.md
2026-07-06 14:42:07 -04:00

83 lines
2.9 KiB
Markdown

# Bead bf-688te: Implement spawn_mcp_server() function with RAII guard
## Status: COMPLETE ✅
## Summary
The `spawn_mcp_server()` function and `McpServerGuard` RAII guard were already fully implemented in `/home/coding/pdftract/crates/pdftract-cli/tests/TH-05-ssrf-block.rs`.
## Acceptance Criteria - ALL PASS ✅
### ✅ PASS: spawn_mcp_server() function exists and returns a guard type
- **Location**: Lines 83-99
- **Implementation**:
```rust
fn spawn_mcp_server() -> McpServerGuard {
let child = Command::new(PDFTRACT)
.arg("mcp")
.arg("--stdio")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null()) // Discard stderr to avoid pipe buffer blocking
.spawn()
.expect("Failed to spawn pdftract mcp --stdio");
McpServerGuard::new(child)
}
```
### ✅ PASS: Guard type implements Drop and kills the child process
- **Location**: Lines 27-81 (McpServerGuard struct)
- **Drop implementation** (lines 52-81):
1. Closes stdin to signal EOF (graceful shutdown)
2. Waits with bounded 200ms timeout using `try_wait()`
3. Falls back to `kill()` if graceful shutdown fails
4. Never uses blocking `wait()`
### ✅ PASS: No orphaned processes after guard is dropped
- RAII guard ensures cleanup on drop (even if test panics)
- Bounded waits prevent hanging (200ms timeout)
- Force-kill fallback ensures termination
### ✅ PASS: Stdio::null() for stderr to avoid pipe buffer blocking
- Line 94: `.stderr(Stdio::null())`
- Comment explains the rationale: "Discard stderr to avoid pipe buffer blocking"
### N/A: Binds to port :0 if applicable
- Not applicable for stdio mode (uses stdin/stdout pipes, not network ports)
- If HTTP bind mode were used, would bind to `127.0.0.1:0` and read back the assigned port
## Test Coverage
The implementation is used by 7 test cases in the same file:
1. `test_ipv4_loopback_blocked` (line 200)
2. `test_ipv4_wildcard_blocked` (line 279)
3. `test_cloud_metadata_blocked` (line 346)
4. `test_rfc1918_private_blocked` (line 413)
5. `test_ipv6_loopback_blocked` (line 480)
6. `test_http_scheme_rejected` (line 548)
7. `test_no_network_connection_attempted` (line 619)
All tests properly use the RAII guard pattern:
```rust
let mut server = spawn_mcp_server();
// ... use server ...
// Guard automatically drops here, cleaning up the child process
```
## Verification
- ✅ Code review confirms all acceptance criteria met
- ✅ Implementation follows TH-03 lessons (bounded waits, no blocking wait(), Stdio::null())
- ✅ RAII pattern ensures cleanup even on panic
- ✅ No orphaned processes possible
## References
- File: `/home/coding/pdftract/crates/pdftract-cli/tests/TH-05-ssrf-block.rs`
- Related: TH-03 (MCP server without authentication)
- Plan: Threat Model (TH-05 SSRF protection)
## Conclusion
The task is **already complete**. The implementation is production-ready and follows all best practices for subprocess management in Rust test code.