# Security Review

> Security Review

- Skill: `openjiuwen-ai/security-review` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add openjiuwen-ai/security-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/openjiuwen-ai/security-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security, AI & ML
- Author: openJiuwen-ai (https://skillmd.com/u/openjiuwen-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/openjiuwen-ai/security-review

---


# Security Review

Comprehensive security checklist for agent-core. Run through all 10
categories before any security-sensitive change or PR.

See `.claude/rules/python/security.md` for tool-specific guidance (bandit, pip-audit).
See `.claude/rules/security.md` for credential, sandbox, and shell execution rules.

## 1. Secrets Management

**Rule:** Credentials never enter source code.

- [ ] No API keys, tokens, or passwords hardcoded in `.py` files
- [ ] All secrets loaded from environment variables via `os.getenv()`
- [ ] `.env` files not committed (already in `.gitignore` — do not remove)
- [ ] `settings.json` deny rules block `Read(./.env)` and `Read(./**/secrets/**)`
- [ ] Test files use mock defaults: `os.getenv("KEY", "mock-key-for-tests")`

```python
# Bad
API_KEY = "sk-1234567890abcdef"

# Good
import os
API_KEY = os.getenv("OPENAI_API_KEY")  # Must be set in environment
```

## 2. Input Validation

**Rule:** Validate all external input before use.

- [ ] User-supplied file paths checked with `safe_path` utilities
- [ ] Paths rejected if they contain `..` or resolve outside allowed scope
- [ ] Shell commands constructed via parameterized APIs, not string concatenation
- [ ] URL parameters and query strings sanitized before use

For `sys_operation`:

```python
from openjiuwen.core.common.security import safe_path

def execute_command(user_path: str, working_dir: Path) -> None:
    validated = safe_path(user_path, allowed_base=working_dir)
    if validated is None:
        raise SecurityError(f"Path outside allowed scope: {user_path}")
    # Proceed with validated path
```

## 3. SQL Injection

**Rule:** Use parameterized queries for all database operations.

- [ ] No string interpolation in SQL: `f"SELECT * FROM {table}"` is forbidden
- [ ] All SQL uses parameterized placeholders: `"WHERE id = ?", (id,)`
- [ ] Table/column names validated against an allowlist if dynamic

```python
# Bad
cursor.execute(f"SELECT * FROM {table_name} WHERE id = {user_id}")

# Good
cursor.execute(
    "SELECT * FROM sessions WHERE id = ?",
    (session_id,)
)
```

## 4. Authentication and RBAC

**Rule:** Access control must be enforced server-side, not just client-side.

- [ ] All agent capabilities gated behind permission checks in `core/security/`
- [ ] Guardrails in `openjiuwen/core/security/` verify permissions before execution
- [ ] No capability bypassed by missing checks on alternate code paths
- [ ] Resource limits enforced (rate limiting, concurrent request limits)

## 5. Prompt Injection

**Rule:** `openjiuwen/harness/prompts/` must guard against injected user content.

- [ ] User-provided strings never concatenated directly into system prompts without sanitization
- [ ] Prompt templates use placeholder isolation (separate user content from instruction)
- [ ] Rail outputs validated before being passed to downstream components
- [ ] The security rail (`openjiuwen/harness/rails/`) correctly blocks dangerous patterns

Prompt injection is agent-core's most unique security concern. Attackers may
try to inject instructions into conversation history to manipulate agent behavior:

```python
# Bad — user content injected into system prompt
system_prompt = f"You are a helpful assistant. User said: {user_message}"

# Good — user content kept in separate context slot
system_prompt = SYSTEM_INSTRUCTIONS
context = {
    "user_message": sanitize_for_display(user_message),
    "conversation_history": conversation,
}
```

## 6. CSRF / Request Validation

**Rule:** All mutating requests include validation.

- [ ] `core/session/` validates that requests originate from legitimate sessions
- [ ] Session IDs are non-guessable (use `secrets.token_urlsafe()`)
- [ ] Session state changes are idempotent or protected by transaction semantics
- [ ] No state-modifying operations accessible without session context

## 7. Rate Limiting

**Rule:** Protect `core/runner/` and `core/runner/` resources from exhaustion.

- [ ] `Runner.resource_mgr` enforces limits on concurrent agent executions
- [ ] Memory usage bounded for long-running sessions
- [ ] Compaction triggers prevent unbounded context growth
- [ ] Tool call frequency limits enforced per session

## 8. Sensitive Data in Logs

**Rule:** Logs must not expose credentials, tokens, or sensitive data.

- [ ] No API keys, tokens, or passwords in log output
- [ ] Use structured logging with explicit field names, not f-string interpolation
- [ ] Error messages do not include sensitive user data
- [ ] `openjiuwen.core.common.logging` used instead of `print()`

```python
# Bad
logger.info(f"Authenticated user {user_id} with token {token}")

# Good
logger.info("User authenticated", extra={"user_id": user_id})
```

## 9. Dependency Security

**Rule:** All dependencies scanned before merging PRs.

- [ ] New dependencies reviewed for known CVEs: `pip-audit`
- [ ] New network-facing dependencies reviewed for security implications
- [ ] `bandit -r openjiuwen/ -ll` passes (no HIGH/CRITICAL findings)
- [ ] Third-party code in `core/sys_operation/` and `core/security/` minimized

```bash
# Run before merging dependency changes
pip-audit
bandit -r openjiuwen/ -ll
```

## 10. Sandbox Isolation

**Rule:** `core/sys_operation/sandbox/` must provide genuine isolation.

- [ ] File operations respect path scoping (no escape via `../`)
- [ ] Shell execution runs in a restricted environment
- [ ] Network access is explicitly allowed/denied, not default-open
- [ ] Cleanup runs after every operation, even on failure
- [ ] Interrupt/confirm flows preserved for user-facing operations

For sandbox implementations, verify:

```python
# Path isolation
def sandbox_read(path: Path, allowed_base: Path) -> str:
    resolved = (allowed_base / path).resolve()
    if not resolved.is_relative_to(allowed_base):
        raise SecurityError(f"Escape attempt: {path}")
    return resolved.read_text()
```

## Pre-Review Checklist

Before marking a security-sensitive PR as ready for review, run through
all 10 categories above. Document the review in the PR description:

```
Security Review
===============
Secrets:        PASS (no hardcoded credentials)
Input Val:      PASS (safe_path used for all user paths)
SQL Injection:  PASS (parameterized queries only)
Auth/RBAC:      PASS (guardrails enforce permissions)
Prompt Inject:  PASS (user content isolated from system prompts)
CSRF:           PASS (session IDs are non-guessable)
Rate Limiting:  PASS (resource_mgr enforces limits)
Log Safety:     PASS (no credentials in structured logs)
Dependencies:   PASS (bandit + pip-audit clean)
Sandbox:        PASS (path scoping verified)
```

For changes to `core/security/`, `core/sys_operation/`, or
`openjiuwen/extensions/sys_operation/sandbox/`, request a dedicated
security review from a second reviewer.

