Back to skills
SKILL.md
2504 Design Ba178a27
ASecurity- **Feature**: security-hardening-addendum - **Status**: APPROVED - **Created**: 2026-02-15 - **Author**: Factory Design Mode - **Parent Branch**: fix/codeql-scanning-alerts
- 9 stars
- 0 votes
- 0 copies
- 0 views
- Added October 11, 2026
Works with
Security analysis
100/100npx -y skills add tools-only/X-Skills --skill 2504-design_ba178a27 --agent claude-codeAre you the author of 2504 Design Ba178a27?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tools-only-2504-design-ba178a27)# Technical Design: security-hardening-addendum
## Metadata
- **Feature**: security-hardening-addendum
- **Status**: APPROVED
- **Created**: 2026-02-15
- **Author**: Factory Design Mode
- **Parent Branch**: fix/codeql-scanning-alerts
---
## 1. Overview
### 1.1 Summary
Apply 4 defense-in-depth hardening fixes across 2 files: sanitize PR titles at output boundaries in `pr_engine.py` (2 locations) and eliminate temp file permission race windows in `history_engine.py` (2 locations). All changes are minimal, non-behavioral, and use existing patterns/functions already in the codebase.
### 1.2 Goals
- PR title sanitized at output boundary (consistency with body sanitization)
- Temp rebase scripts created with 0o700 from the start (no race window)
### 1.3 Non-Goals
- Refactoring `_sanitize_pr_content()` itself
- Adding new tests (existing suite covers these paths)
- Addressing any other security findings
---
## 2. Architecture
### 2.1 High-Level Design
No architectural changes. Two localized hardening patterns applied to existing code:
```
PR Title Flow (BEFORE):
raw title → subprocess.run() args list
raw title → draft markdown file
PR Title Flow (AFTER):
raw title → _sanitize_pr_content() → subprocess.run() args list
raw title → _sanitize_pr_content() → draft markdown file
Temp Script Flow (BEFORE):
NamedTemporaryFile(delete=False) → write → os.chmod(0o700)
[race window: default umask permissions until chmod]
Temp Script Flow (AFTER):
mkstemp() → os.fchmod(fd, 0o700) → os.fdopen(fd) → write
[no race window: permissions set before any content written]
```
### 2.2 Component Breakdown
| Component | Responsibility | Files |
|-----------|---------------|-------|
| PRCreator.create() | PR creation via gh CLI | zerg/git/pr_engine.py |
| PRCreator._save_draft() | Draft file fallback | zerg/git/pr_engine.py |
| HistoryEngine._squash_commits() | Git rebase squash script | zerg/git/history_engine.py |
| HistoryEngine._reorder_commits() | Git rebase reorder script | zerg/git/history_engine.py |
### 2.3 Data Flow
- **PR title**: `pr_data["title"]` → `_sanitize_pr_content()` → `html.escape()` → safe string
- **Temp script**: `tempfile.mkstemp()` returns `(fd, path)` → `os.fchmod(fd, 0o700)` → `os.fdopen(fd, "w")` → write script content
---
## 3. Detailed Design
### 3.1 PR Title Sanitization (pr_engine.py)
**Location 1 — `create()` method, line 509:**
```python
# BEFORE:
title = pr_data.get("title", "Update")
# AFTER:
title = _sanitize_pr_content(pr_data.get("title", "Update"))
```
**Location 2 — `_save_draft()` method, line 586:**
```python
# BEFORE:
content = f"# {pr_data.get('title', 'PR Draft')}\n\n{pr_data.get('body', '')}"
# AFTER:
content = f"# {_sanitize_pr_content(pr_data.get('title', 'PR Draft'))}\n\n{pr_data.get('body', '')}"
```
### 3.2 Temp File Permission Hardening (history_engine.py)
**Location 1 — `_squash_commits()`, lines 546-570:**
```python
# BEFORE:
with tempfile.NamedTemporaryFile(
mode="w", suffix=".py", delete=False, prefix="zerg_rebase_",
) as script_file:
script_path = script_file.name
# ... write content ...
os.chmod(script_path, 0o700)
# AFTER:
fd, script_path = tempfile.mkstemp(suffix=".py", prefix="zerg_rebase_")
os.fchmod(fd, 0o700)
with os.fdopen(fd, "w") as script_file:
# ... write content (unchanged) ...
# os.chmod removed — permissions already set via fchmod
```
**Location 2 — `_reorder_commits()`, lines 633-662:**
```python
# BEFORE:
with tempfile.NamedTemporaryFile(
mode="w", suffix=".py", delete=False, prefix="zerg_reorder_",
) as script_file:
script_path = script_file.name
# ... write content ...
os.chmod(script_path, 0o700)
# AFTER:
fd, script_path = tempfile.mkstemp(suffix=".py", prefix="zerg_reorder_")
os.fchmod(fd, 0o700)
with os.fdopen(fd, "w") as script_file:
# ... write content (unchanged) ...
# os.chmod removed — permissions already set via fchmod
```
---
## 4. Key Decisions
### 4.1 Use `_sanitize_pr_content()` for Title (Not a New Function)
**Context**: PR title needs sanitization. Could create a title-specific function or reuse existing.
**Decision**: Reuse `_sanitize_pr_content()` — it uses `html.escape()` which is safe for all text.
**Rationale**: Function already exists, is well-tested via body path, and provides consistent behavior.
### 4.2 Use `mkstemp` + `fchmod` (Not `opener` Kwarg)
**Context**: Need atomic secure file creation. Options: `mkstemp`+`fchmod`, `os.open`+`os.fdopen`, or `open(opener=...)`.
**Decision**: `mkstemp` + `fchmod` — simplest, most explicit, stdlib-only.
**Rationale**: `mkstemp` returns an fd, `fchmod` sets permissions before any content is written. Clear pattern, no race window.
---
## 5. Implementation Plan
### 5.1 Phase Summary
| Phase | Tasks | Parallel | Est. Time |
|-------|-------|----------|-----------|
| Core | 2 | Yes | 3 min |
| Quality | 1 | No | 2 min |
### 5.2 File Ownership
| File | Task ID | Operation |
|------|---------|-----------|
| zerg/git/pr_engine.py | TASK-001 | modify |
| zerg/git/history_engine.py | TASK-002 | modify |
| CHANGELOG.md | TASK-003 | modify |
### 5.3 Dependency Graph
```
TASK-001 [PR title sanitization] ──┐
├──▶ TASK-003 [CHANGELOG + verify]
TASK-002 [Temp file hardening] ───┘
```
---
## 6. Risk Assessment
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| `os.fchmod` not available on platform | Very Low | Med | Python stdlib on all POSIX; not used on Windows |
| `_sanitize_pr_content` over-escapes title | Low | Low | Uses `html.escape()` which is idempotent and safe |
| Existing tests break | Very Low | Med | Changes are non-behavioral; run full suite |
---
## 7. Testing Strategy
### 7.1 Verification
- `python -m pytest tests/ --timeout=120` — full suite green
- `ruff check zerg/git/pr_engine.py zerg/git/history_engine.py` — clean
### 7.2 No New Tests Required
Existing test suite covers PR creation and history engine paths. Changes are non-behavioral.
---
## 8. Parallel Execution Notes
### 8.1 Safe Parallelization
- TASK-001 and TASK-002 modify different files — fully parallel
- TASK-003 depends on both completing
### 8.2 Recommended Workers
- Minimum: 1 worker (sequential)
- Optimal: 2 workers (one per file)
- Maximum: 2 workers
### 8.3 Estimated Duration
- Single worker: ~5 min
- With 2 workers: ~3 min
- Speedup: 1.7x
---
## 9. Approval
| Role | Name | Date | Signature |
|------|------|------|-----------|
| Architecture | | | PENDING |
| Engineering | | | PENDING |
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.