Spaces:
Sleeping
Sleeping
fix: mirror reference project structure - openenv section, POST /grader, openenv tag, HEALTHCHECK
Browse files- Dockerfile +19 -11
- README.md +10 -297
- openenv.yaml +99 -47
- server/environment.py +5 -1
Dockerfile
CHANGED
|
@@ -1,27 +1,35 @@
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
|
| 3 |
-
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 4 |
-
PYTHONUNBUFFERED=1 \
|
| 5 |
-
PIP_NO_CACHE_DIR=1
|
| 6 |
-
|
| 7 |
WORKDIR /app
|
| 8 |
|
|
|
|
| 9 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 10 |
bash \
|
| 11 |
ca-certificates \
|
| 12 |
curl \
|
|
|
|
| 13 |
git \
|
| 14 |
&& rm -rf /var/lib/apt/lists/*
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
|
| 21 |
-
|
|
|
|
| 22 |
|
| 23 |
-
|
|
|
|
| 24 |
|
|
|
|
| 25 |
EXPOSE 7860
|
| 26 |
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
+
# System dependencies
|
| 6 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 7 |
bash \
|
| 8 |
ca-certificates \
|
| 9 |
curl \
|
| 10 |
+
gcc \
|
| 11 |
git \
|
| 12 |
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
|
| 14 |
+
# Install Python dependencies
|
| 15 |
+
COPY requirements.txt .
|
| 16 |
+
RUN pip install --no-cache-dir -r requirements.txt && \
|
| 17 |
+
pip install --no-cache-dir git-filter-repo
|
| 18 |
|
| 19 |
+
# Copy application code
|
| 20 |
+
COPY . .
|
| 21 |
|
| 22 |
+
# Install the package itself
|
| 23 |
+
RUN pip install --no-cache-dir -e .
|
| 24 |
|
| 25 |
+
# HF Spaces default port
|
| 26 |
EXPOSE 7860
|
| 27 |
|
| 28 |
+
# Health check
|
| 29 |
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=5 \
|
| 30 |
+
CMD curl -f http://localhost:7860/health || exit 1
|
| 31 |
+
|
| 32 |
+
ENV PYTHONUNBUFFERED=1
|
| 33 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 34 |
+
|
| 35 |
+
CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
|
@@ -4,304 +4,17 @@ emoji: 🔐
|
|
| 4 |
colorFrom: indigo
|
| 5 |
colorTo: red
|
| 6 |
sdk: docker
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
# SecretsAuditEnv
|
| 11 |
|
| 12 |
-
A
|
| 13 |
-
|
| 14 |
-
## Why This Exists
|
| 15 |
-
|
| 16 |
-
Production secret leaks remain a top-5 cause of cloud breaches. Developers commit API keys, push `.env` files, or leave credentials in migration scripts — then scramble to rotate and rewrite history. Existing linting tools flag secrets but don't fix them; LLM agents can, but there's no standardized benchmark to measure how well. **SecretsAuditEnv** fills that gap with a 13-task curriculum spanning trivial single-file fixes to multi-service cascading leaks with git-history rewriting, complete with a deterministic grading pipeline and an anti-gaming reward function.
|
| 17 |
-
|
| 18 |
-
---
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
---
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
## Task Curriculum
|
| 25 |
-
|
| 26 |
-
All 13 tasks are defined in `tasks/` with full metadata in each `task.json`. Secret types are detected by `graders/security.py` using regex patterns for AWS keys, GitHub tokens, Firebase keys, connection strings, private keys, SQL passwords, assignment secrets, and base64-encoded variants.
|
| 27 |
-
|
| 28 |
-
| ID | Difficulty | Title | Description | Scan Mode | Visibility Tiers | Conflict Map |
|
| 29 |
-
|----|-----------|-------|-------------|-----------|-------------------|--------------|
|
| 30 |
-
| 1 | Easy | Cloud Provisioning | Hardcoded AWS Access Key in config.py | `dir` | surface, surface, shallow | — |
|
| 31 |
-
| 2 | Easy | Database Layer | Password embedded in a raw SQL connection string | `dir` | surface, surface, shallow | — |
|
| 32 |
-
| 3 | Easy | Frontend Config | Firebase API key exposed in a client-side config file | `dir` | surface, surface, shallow | — |
|
| 33 |
-
| 4 | Easy | System Logging | Debug logging leaks a user token | `dir` | surface, surface, shallow | — |
|
| 34 |
-
| 5 | Easy | Git Basics | A tracked .env file leaks credentials in the working tree | `dir` | surface, surface, shallow | — |
|
| 35 |
-
| 6 | Medium | Utility Module | A base64-encoded auth token hides in utils.py | `dir` | surface, shallow, deep | — |
|
| 36 |
-
| 7 | Medium | CI/CD Pipeline | A deployment workflow prints a secret directly to logs | `dir` | surface, shallow, deep | — |
|
| 37 |
-
| 8 | Medium | Noise Filtering | High-entropy dummy values are mixed with one real secret in TOML | `dir` | surface, shallow, deep | — |
|
| 38 |
-
| 9 | Medium | DB Migration | A legacy migration embeds administrator credentials | `dir` | surface, shallow, deep | — |
|
| 39 |
-
| 10 | Medium | Deployment | A multiline RSA private key is embedded in a shell script | `dir` | surface, shallow, deep | — |
|
| 40 |
-
| 11 | Hard | Microservices | The same API key is duplicated across five services | `dir` | surface, deep, cascading | ✓ |
|
| 41 |
-
| 12 | Hard | Deep Logic | A secret is embedded as a local variable inside a function | `dir` | surface, deep, cascading | ✓ |
|
| 42 |
-
| 13 | Hard | Legacy Audit | A secret was committed in v1.0 and still exists in Git history | `git` | surface, deep, cascading | ✓ |
|
| 43 |
-
|
| 44 |
-
**Scan modes**: `dir` scans the working directory only. `git` scans the full git commit history (all revisions), so agents must use `git filter-repo` to clean history — deleting files won't work.
|
| 45 |
-
|
| 46 |
-
---
|
| 47 |
-
|
| 48 |
-
## Secret Visibility Tiers
|
| 49 |
-
|
| 50 |
-
Secrets are not all visible at episode start. The environment implements a 4-tier progressive disclosure system defined per-secret in `task.json`:
|
| 51 |
-
|
| 52 |
-
| Tier | When Visible | Typical Use |
|
| 53 |
-
|------|-------------|-------------|
|
| 54 |
-
| `SURFACE` | Immediately on `/reset` | Obvious hardcoded keys in source files |
|
| 55 |
-
| `SHALLOW` | After agent calls `inspect_file <path>` | Secrets that require reading the file to notice |
|
| 56 |
-
| `DEEP` | After `inspect_git_history` or `inspect_encoded` | Secrets in git commits or base64-encoded blobs |
|
| 57 |
-
| `CASCADING` | After a specified trigger secret is fixed | Secrets that only become relevant after another is remediated |
|
| 58 |
-
|
| 59 |
-
Hard tasks (11–13) include **conflict maps** that encode dependencies: fixing secret `s1` may *reveal* `s3`, while `s2` may *block* `s3` until resolved.
|
| 60 |
-
|
| 61 |
-
---
|
| 62 |
-
|
| 63 |
-
## Reward Formula
|
| 64 |
-
|
| 65 |
-
Defined in `graders/reward.py`. Computed fresh after every `/step`:
|
| 66 |
-
|
| 67 |
-
```
|
| 68 |
-
detection_score = (initial_leaks - current_leaks) / initial_leaks
|
| 69 |
-
base = 0.4 × detection_score + 0.6 × detection_score # (remediation = detection for now)
|
| 70 |
-
health_score = pytest_passed / pytest_total # 0.0 if errors > 0 or total == 0
|
| 71 |
-
efficiency = 0.15 × max(0, 1 - steps_taken / step_budget) # decays linearly per step
|
| 72 |
-
|
| 73 |
-
if base > 0:
|
| 74 |
-
total_reward = min(1.0, base × health_score + efficiency)
|
| 75 |
-
else:
|
| 76 |
-
total_reward = 0.0 # no free points for doing nothing
|
| 77 |
-
```
|
| 78 |
-
|
| 79 |
-
**Key properties:**
|
| 80 |
-
- Reward is **0.0 until the agent actually fixes something** — read-only commands like `cat` or `ls` cannot earn reward
|
| 81 |
-
- **Health gate**: breaking the test suite (e.g., deleting imports) multiplies reward toward zero
|
| 82 |
-
- **Efficiency bonus** (0.0–0.15): rewards agents that solve in fewer steps. Decays to 0 at step budget
|
| 83 |
-
- **Step budgets**: Easy = 10, Medium = 20, Hard = 30
|
| 84 |
-
|
| 85 |
-
---
|
| 86 |
-
|
| 87 |
-
## Action Space
|
| 88 |
-
|
| 89 |
-
Actions are sent as the `action` field in `POST /step`. Two categories:
|
| 90 |
-
|
| 91 |
-
### Structured Actions (intercepted before bash)
|
| 92 |
-
|
| 93 |
-
| Action | Format | Effect |
|
| 94 |
-
|--------|--------|--------|
|
| 95 |
-
| `inspect_file` | `inspect_file <path>` | Marks file as inspected → unlocks SHALLOW secrets for that path |
|
| 96 |
-
| `inspect_git_history` | `inspect_git_history [path]` | Scans git history → unlocks all DEEP secrets requiring git inspection |
|
| 97 |
-
| `inspect_encoded` | `inspect_encoded <path> [line]` | Decodes base64 blobs → unlocks DEEP encoded secrets for that path |
|
| 98 |
-
|
| 99 |
-
### Bash Commands (executed in workspace via `/usr/bin/bash -lc`)
|
| 100 |
-
|
| 101 |
-
Any string that doesn't match a structured prefix is executed as a bash command in the task workspace. Common patterns:
|
| 102 |
-
|
| 103 |
-
- `cat config.py` — read file contents
|
| 104 |
-
- `sed -i 's/AKIA.../os.getenv("AWS_KEY")/' config.py` — redact a secret
|
| 105 |
-
- `git filter-repo --replace-text <(echo 'ghp_xxx==>REDACTED') --force` — clean git history
|
| 106 |
-
- `gitleaks detect --no-git --source .` — run leak scanner
|
| 107 |
-
|
| 108 |
-
Commands time out after 90 seconds. Exit code, stdout, and stderr are returned in the observation.
|
| 109 |
-
|
| 110 |
-
---
|
| 111 |
-
|
| 112 |
-
## Observation Keys
|
| 113 |
-
|
| 114 |
-
Every `/step` and `/reset` response returns a `session` object with these fields (also listed in `openenv.yaml`):
|
| 115 |
-
|
| 116 |
-
| Key | Type | Description |
|
| 117 |
-
|-----|------|-------------|
|
| 118 |
-
| `visible_secrets` | `list[dict]` | Secrets the agent can currently see (filtered by visibility tier) |
|
| 119 |
-
| `hidden_count_hint` | `int` | Number of secrets not yet visible — tells agent more exist |
|
| 120 |
-
| `ranked_actions` | `list[dict]` | Top-5 heuristic action suggestions sorted by priority (0.0–1.0) |
|
| 121 |
-
| `top_blocker` | `string` | One-sentence description of the highest-priority next action |
|
| 122 |
-
| `step_budget` | `int` | Total step budget for this difficulty tier |
|
| 123 |
-
| `steps_taken` | `int` | Steps consumed so far |
|
| 124 |
-
| `steps_remaining` | `int` | `step_budget - steps_taken` |
|
| 125 |
-
| `efficiency_bonus` | `float` | Current efficiency bonus value (decays each step) |
|
| 126 |
-
| `conflict_map` | `dict` | Dependency graph between visible secrets (reveals/blocks relationships) |
|
| 127 |
-
| `security_score` | `float` | Fraction of initial leaks fixed (0.0–1.0) |
|
| 128 |
-
| `health_score` | `float` | Fraction of pytest tests passing (0.0–1.0) |
|
| 129 |
-
| `reward` | `float` | Composite reward after this step |
|
| 130 |
-
| `observation` | `string` | Combined stdout/stderr from last command + health/security messages |
|
| 131 |
-
| `last_result` | `dict` | Raw action, exit_code, stdout, stderr, timed_out from last command |
|
| 132 |
-
|
| 133 |
-
### Ranked Actions
|
| 134 |
-
|
| 135 |
-
Generated by `server/observation.py` using 5 heuristics:
|
| 136 |
-
1. **Visible unfixed secrets** → priority 0.92 (fix these first)
|
| 137 |
-
2. **Uninspected high-risk files** → priority based on filename suspicion score
|
| 138 |
-
3. **Git history not yet scanned** → priority 0.75 (medium/hard only)
|
| 139 |
-
4. **Hidden secrets remaining** → priority 0.68 (suggest encoded inspection)
|
| 140 |
-
5. **All visible fixed** → priority 0.85 (run gitleaks validation)
|
| 141 |
-
|
| 142 |
-
---
|
| 143 |
-
|
| 144 |
-
## Quick Start
|
| 145 |
-
|
| 146 |
-
### Local Development
|
| 147 |
-
|
| 148 |
-
```bash
|
| 149 |
-
# Install dependencies
|
| 150 |
-
pip install -r requirements.txt
|
| 151 |
-
|
| 152 |
-
# Generate task workspaces
|
| 153 |
-
python tools/generate_tasks.py
|
| 154 |
-
|
| 155 |
-
# Start the server
|
| 156 |
-
uvicorn server.app:app --host 0.0.0.0 --port 7860
|
| 157 |
-
|
| 158 |
-
# Open the debug UI
|
| 159 |
-
open http://localhost:7860/web
|
| 160 |
-
|
| 161 |
-
# Run the agent (requires LLM API)
|
| 162 |
-
export API_BASE_URL="https://openrouter.ai/api/v1"
|
| 163 |
-
export HF_TOKEN="your-api-key"
|
| 164 |
-
export MODEL_NAME="nvidia/nemotron-3-super-120b-a12b:free"
|
| 165 |
-
export ENV_URL="http://localhost:7860"
|
| 166 |
-
python inference.py --task-id 1
|
| 167 |
-
```
|
| 168 |
-
|
| 169 |
-
### Docker
|
| 170 |
-
|
| 171 |
-
```bash
|
| 172 |
-
docker build -t secretsauditenv .
|
| 173 |
-
docker run -p 7860:7860 secretsauditenv
|
| 174 |
-
# Server auto-generates tasks and starts on port 7860
|
| 175 |
-
```
|
| 176 |
-
|
| 177 |
-
The Dockerfile installs Python 3.11-slim with bash, git, git-filter-repo, and all pip dependencies. The entrypoint runs `tools/generate_tasks.py` then starts uvicorn.
|
| 178 |
-
|
| 179 |
-
---
|
| 180 |
-
|
| 181 |
-
## inference.py — Agent Loop
|
| 182 |
-
|
| 183 |
-
The baseline agent (`inference.py`) implements a single-turn ReAct loop over any OpenAI-compatible API:
|
| 184 |
-
|
| 185 |
-
1. **Reset** — `POST /reset` with the target task ID
|
| 186 |
-
2. **Prompt** — Builds a structured prompt from the current session state (reward, leaks, health, last command output, recent actions)
|
| 187 |
-
3. **Call LLM** — Sends prompt to the model, extracts a bash command from the response
|
| 188 |
-
4. **Normalize** — Strips markdown fences, extracts from XML tool_call formats, filters prose prefixes, enforces atomic (no `&&` or `;` chaining)
|
| 189 |
-
5. **Step** — `POST /step` with the action, reads updated state
|
| 190 |
-
6. **Repeat** until `reward >= 1.0` or `MAX_STEPS` (40) reached
|
| 191 |
-
|
| 192 |
-
**Environment variables:**
|
| 193 |
-
|
| 194 |
-
| Variable | Required | Description |
|
| 195 |
-
|----------|----------|-------------|
|
| 196 |
-
| `API_BASE_URL` | Yes | OpenAI-compatible API endpoint (e.g., `https://openrouter.ai/api/v1`) |
|
| 197 |
-
| `HF_TOKEN` | Yes | API key for the model provider |
|
| 198 |
-
| `MODEL_NAME` | Yes | Model identifier (e.g., `nvidia/nemotron-3-super-120b-a12b:free`) |
|
| 199 |
-
| `ENV_URL` | No | Server URL, defaults to `http://localhost:7860` |
|
| 200 |
-
| `TASK_ID` | No | Default task, overridden by `--task-id` CLI arg |
|
| 201 |
-
|
| 202 |
-
**Anti-loop features:**
|
| 203 |
-
- Detects 3 consecutive identical actions with identical rewards → injects a CRITICAL WARNING into the prompt and forces a different command
|
| 204 |
-
- Filters natural language preambles (e.g., "Let me check...", "Looking at...") before sending to bash
|
| 205 |
-
|
| 206 |
-
---
|
| 207 |
-
|
| 208 |
-
## Grading Pipeline
|
| 209 |
-
|
| 210 |
-
### Security Grader (`graders/security.py`)
|
| 211 |
-
|
| 212 |
-
Custom regex-based scanner (no external tools required). Detects:
|
| 213 |
-
- AWS Access Keys (`AKIA...`)
|
| 214 |
-
- GitHub tokens (`ghp_...`)
|
| 215 |
-
- Firebase API keys (`AIza...`)
|
| 216 |
-
- Service tokens (`tok_live_...`, `sk_test_...`)
|
| 217 |
-
- Private keys (PEM format)
|
| 218 |
-
- SQL connection strings (`postgres://user:pass@host/db`)
|
| 219 |
-
- SQL passwords (`PASSWORD 'value'`)
|
| 220 |
-
- High-entropy assignment secrets (Shannon entropy ≥ 3.2)
|
| 221 |
-
- Base64-encoded variants of all the above
|
| 222 |
-
|
| 223 |
-
Supports `.gitleaks.toml` allowlists. For `scan_mode: git`, scans all commits via `git rev-list --all`.
|
| 224 |
-
|
| 225 |
-
**Anti-gaming**: if an agent deletes `.git`, the grader recovers by creating a fresh snapshot and scanning that — the secret still gets found.
|
| 226 |
-
|
| 227 |
-
### Health Grader (`graders/health.py`)
|
| 228 |
-
|
| 229 |
-
Runs `pytest -q --junitxml` in the task workspace and parses the JUnit XML report. Score = `passed / total`. Returns 0.0 if any errors or if pytest times out (60s default).
|
| 230 |
-
|
| 231 |
-
---
|
| 232 |
-
|
| 233 |
-
## Web Debug UI
|
| 234 |
-
|
| 235 |
-
Available at `GET /web`. A single-page dark-mode dashboard served from `server/web_ui.html` that lets you:
|
| 236 |
-
- Start any of the 13 tasks with one click
|
| 237 |
-
- Send structured actions or raw bash commands
|
| 238 |
-
- View real-time metrics (reward, leaks, hidden count, steps, efficiency, health)
|
| 239 |
-
- See ranked action suggestions and conflict maps
|
| 240 |
-
- Browse visible secrets and full observation text
|
| 241 |
-
- Review action history
|
| 242 |
-
|
| 243 |
-
No external dependencies — pure HTML/CSS/JS.
|
| 244 |
-
|
| 245 |
-
---
|
| 246 |
-
|
| 247 |
-
## Validation
|
| 248 |
-
|
| 249 |
-
```bash
|
| 250 |
-
# Run the environment test suite (34 tests)
|
| 251 |
-
python -m pytest tests/ -v
|
| 252 |
-
|
| 253 |
-
# Run the submission validator (checks connection, Docker build, spec compliance, reward integrity)
|
| 254 |
-
bash validate-submission.sh
|
| 255 |
-
```
|
| 256 |
-
|
| 257 |
-
The validator checks:
|
| 258 |
-
1. **Connection** — `/reset` returns HTTP 200
|
| 259 |
-
2. **Docker portability** — no absolute host paths leak into the image
|
| 260 |
-
3. **Spec compliance** — `spec.md` exists, all 13 task directories present, `openenv validate` passes
|
| 261 |
-
4. **Reward integrity** — smoke test runs `inference.py` for 2 steps and verifies `[START]/[STEP]/[END]` log format
|
| 262 |
-
|
| 263 |
-
---
|
| 264 |
-
|
| 265 |
-
## Project Structure
|
| 266 |
-
|
| 267 |
-
```
|
| 268 |
-
.
|
| 269 |
-
├── server/
|
| 270 |
-
│ ├── app.py # FastAPI routes (/reset, /step, /state, /tasks, /web)
|
| 271 |
-
│ ├── environment.py # Core environment logic, visibility tiers, action parsing
|
| 272 |
-
│ ├── observation.py # Ranked action heuristics and top_blocker computation
|
| 273 |
-
│ └── web_ui.html # Debug dashboard (served at /web)
|
| 274 |
-
├── graders/
|
| 275 |
-
│ ├── security.py # Regex-based secret scanner with git history support
|
| 276 |
-
│ ├── health.py # Pytest-based health grader with JUnit parsing
|
| 277 |
-
│ ├── reward.py # Composite reward: security × health + efficiency
|
| 278 |
-
│ ├── gitleaks_eval.py # Spec-aligned wrapper with git integrity reporting
|
| 279 |
-
│ └── health_eval.py # Spec-aligned wrapper with failure messaging
|
| 280 |
-
├── tasks/
|
| 281 |
-
│ ├── easy/task_01..05/ # 5 easy tasks (single-file, surface+shallow secrets)
|
| 282 |
-
│ ├── medium/task_06..10/ # 5 medium tasks (multi-format, surface+shallow+deep)
|
| 283 |
-
│ └── hard/task_11..13/ # 3 hard tasks (cascading, conflict maps, git history)
|
| 284 |
-
├── tests/
|
| 285 |
-
│ ├── test_hidden_leaks.py # 11 tests: visibility tier logic
|
| 286 |
-
│ ├── test_ranked_actions.py # 12 tests: heuristic action suggestions
|
| 287 |
-
│ └── test_reward.py # 11 tests: reward formula and efficiency bonus
|
| 288 |
-
├── tools/
|
| 289 |
-
│ ├── generate_tasks.py # Generates all 13 task workspaces with seeded secrets
|
| 290 |
-
│ ├── check_space_ready.py
|
| 291 |
-
│ ├── prepare_hf_space_bundle.sh
|
| 292 |
-
│ └── prepare_github_upload_bundle.sh
|
| 293 |
-
├── inference.py # Baseline LLM agent loop (OpenAI-compatible)
|
| 294 |
-
├── openenv.yaml # OpenEnv spec declaration
|
| 295 |
-
├── Dockerfile # Python 3.11-slim + git + git-filter-repo
|
| 296 |
-
├── hf_space_entrypoint.sh # Docker entrypoint: generate tasks → start uvicorn
|
| 297 |
-
├── validate-submission.sh # 4-check submission validator
|
| 298 |
-
├── requirements.txt # Pip dependencies
|
| 299 |
-
├── pyproject.toml # Package metadata
|
| 300 |
-
└── spec.md # Environment specification document
|
| 301 |
-
```
|
| 302 |
-
|
| 303 |
-
---
|
| 304 |
-
|
| 305 |
-
## License
|
| 306 |
-
|
| 307 |
-
MIT
|
|
|
|
| 4 |
colorFrom: indigo
|
| 5 |
colorTo: red
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
pinned: true
|
| 9 |
+
tags:
|
| 10 |
+
- openenv
|
| 11 |
+
- reinforcement-learning
|
| 12 |
+
- secret-detection
|
| 13 |
+
- cybersecurity
|
| 14 |
+
- benchmark
|
| 15 |
+
- ai-safety
|
| 16 |
---
|
| 17 |
|
| 18 |
# SecretsAuditEnv
|
| 19 |
|
| 20 |
+
A 13-task RL benchmark for AI agents that find and fix secret leaks in git-backed codebases.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
openenv.yaml
CHANGED
|
@@ -1,74 +1,126 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
app: server.app:app
|
| 8 |
-
port: 7860
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
tasks:
|
| 11 |
- id: task_1
|
| 12 |
-
name:
|
| 13 |
-
|
| 14 |
-
|
|
|
|
| 15 |
|
| 16 |
- id: task_2
|
| 17 |
-
name:
|
| 18 |
-
|
| 19 |
-
|
|
|
|
| 20 |
|
| 21 |
- id: task_3
|
| 22 |
-
name:
|
| 23 |
-
|
| 24 |
-
|
|
|
|
| 25 |
|
| 26 |
- id: task_4
|
| 27 |
-
name:
|
| 28 |
-
|
| 29 |
-
|
|
|
|
| 30 |
|
| 31 |
- id: task_5
|
| 32 |
-
name:
|
| 33 |
-
|
| 34 |
-
|
|
|
|
| 35 |
|
| 36 |
- id: task_6
|
| 37 |
-
name:
|
| 38 |
-
|
| 39 |
-
|
|
|
|
| 40 |
|
| 41 |
- id: task_7
|
| 42 |
-
name:
|
| 43 |
-
|
| 44 |
-
|
|
|
|
| 45 |
|
| 46 |
- id: task_8
|
| 47 |
-
name:
|
| 48 |
-
|
| 49 |
-
|
|
|
|
| 50 |
|
| 51 |
- id: task_9
|
| 52 |
-
name:
|
| 53 |
-
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
- id: task_10
|
| 57 |
-
name:
|
| 58 |
-
|
| 59 |
-
|
|
|
|
| 60 |
|
| 61 |
- id: task_11
|
| 62 |
-
name:
|
| 63 |
-
|
| 64 |
-
|
|
|
|
| 65 |
|
| 66 |
- id: task_12
|
| 67 |
-
name:
|
| 68 |
-
|
| 69 |
-
|
|
|
|
| 70 |
|
| 71 |
- id: task_13
|
| 72 |
-
name:
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: secrets-audit-env
|
| 2 |
+
description: |
|
| 3 |
+
A 13-task RL benchmark for AI agents that find and fix secret leaks
|
| 4 |
+
in git-backed codebases. Agents interact via bash commands and
|
| 5 |
+
structured inspection actions, graded on security, code health,
|
| 6 |
+
and efficiency.
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
Three difficulty tiers (easy → medium → hard) with deterministic
|
| 9 |
+
per-episode graders.
|
| 10 |
+
|
| 11 |
+
version: 1.0.0
|
| 12 |
+
author: Team R2X
|
| 13 |
+
license: MIT
|
| 14 |
+
|
| 15 |
+
# OpenEnv configuration
|
| 16 |
+
openenv:
|
| 17 |
+
env_type: custom
|
| 18 |
+
|
| 19 |
+
server:
|
| 20 |
+
port: 7860
|
| 21 |
+
host: 0.0.0.0
|
| 22 |
+
|
| 23 |
+
# Required OpenEnv endpoints
|
| 24 |
+
required_endpoints:
|
| 25 |
+
tasks: GET /tasks
|
| 26 |
+
grader: POST /grader
|
| 27 |
+
baseline: POST /baseline
|
| 28 |
+
|
| 29 |
+
entry_points:
|
| 30 |
+
server: server.app:app
|
| 31 |
+
|
| 32 |
+
# Tasks (easy → medium → hard)
|
| 33 |
tasks:
|
| 34 |
- id: task_1
|
| 35 |
+
name: Cloud Provisioning
|
| 36 |
+
difficulty: easy
|
| 37 |
+
description: >
|
| 38 |
+
Hardcoded AWS Access Key in config.py.
|
| 39 |
|
| 40 |
- id: task_2
|
| 41 |
+
name: Database Layer
|
| 42 |
+
difficulty: easy
|
| 43 |
+
description: >
|
| 44 |
+
Password embedded in a raw SQL connection string.
|
| 45 |
|
| 46 |
- id: task_3
|
| 47 |
+
name: Frontend Config
|
| 48 |
+
difficulty: easy
|
| 49 |
+
description: >
|
| 50 |
+
Firebase API key exposed in a client-side config file.
|
| 51 |
|
| 52 |
- id: task_4
|
| 53 |
+
name: System Logging
|
| 54 |
+
difficulty: easy
|
| 55 |
+
description: >
|
| 56 |
+
Debug logging leaks a user token.
|
| 57 |
|
| 58 |
- id: task_5
|
| 59 |
+
name: Git Basics
|
| 60 |
+
difficulty: easy
|
| 61 |
+
description: >
|
| 62 |
+
A tracked .env file leaks credentials in the working tree.
|
| 63 |
|
| 64 |
- id: task_6
|
| 65 |
+
name: Utility Module
|
| 66 |
+
difficulty: medium
|
| 67 |
+
description: >
|
| 68 |
+
A base64-encoded auth token hides in utils.py.
|
| 69 |
|
| 70 |
- id: task_7
|
| 71 |
+
name: CI/CD Pipeline
|
| 72 |
+
difficulty: medium
|
| 73 |
+
description: >
|
| 74 |
+
A deployment workflow prints a secret directly to logs.
|
| 75 |
|
| 76 |
- id: task_8
|
| 77 |
+
name: Noise Filtering
|
| 78 |
+
difficulty: medium
|
| 79 |
+
description: >
|
| 80 |
+
High-entropy dummy values are mixed with one real secret in TOML.
|
| 81 |
|
| 82 |
- id: task_9
|
| 83 |
+
name: DB Migration
|
| 84 |
+
difficulty: medium
|
| 85 |
+
description: >
|
| 86 |
+
A legacy migration embeds administrator credentials.
|
| 87 |
|
| 88 |
- id: task_10
|
| 89 |
+
name: Deployment
|
| 90 |
+
difficulty: medium
|
| 91 |
+
description: >
|
| 92 |
+
A multiline RSA private key is embedded in a shell script.
|
| 93 |
|
| 94 |
- id: task_11
|
| 95 |
+
name: Microservices
|
| 96 |
+
difficulty: hard
|
| 97 |
+
description: >
|
| 98 |
+
The same API key is duplicated across five services.
|
| 99 |
|
| 100 |
- id: task_12
|
| 101 |
+
name: Deep Logic
|
| 102 |
+
difficulty: hard
|
| 103 |
+
description: >
|
| 104 |
+
A secret is embedded as a local variable inside a function.
|
| 105 |
|
| 106 |
- id: task_13
|
| 107 |
+
name: Legacy Audit
|
| 108 |
+
difficulty: hard
|
| 109 |
+
description: >
|
| 110 |
+
A secret was committed in v1.0 and still exists in Git history.
|
| 111 |
+
|
| 112 |
+
# Dependencies
|
| 113 |
+
dependencies:
|
| 114 |
+
- openenv-core>=0.2.0
|
| 115 |
+
- fastapi>=0.100.0
|
| 116 |
+
- uvicorn>=0.23.0
|
| 117 |
+
- requests>=2.31.0
|
| 118 |
+
- openai>=1.0.0
|
| 119 |
+
|
| 120 |
+
# Tags for discovery
|
| 121 |
+
tags:
|
| 122 |
+
- secret-detection
|
| 123 |
+
- cybersecurity
|
| 124 |
+
- rl-environment
|
| 125 |
+
- code-security
|
| 126 |
+
- benchmark
|
server/environment.py
CHANGED
|
@@ -124,7 +124,11 @@ class SecretsAuditEnvironment:
|
|
| 124 |
tasks: list[dict[str, Any]] = []
|
| 125 |
for task_file in sorted(self.tasks_root.glob("*/task_*/task.json")):
|
| 126 |
with task_file.open("r", encoding="utf-8") as handle:
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
return tasks
|
| 129 |
|
| 130 |
def state(self) -> dict[str, Any]:
|
|
|
|
| 124 |
tasks: list[dict[str, Any]] = []
|
| 125 |
for task_file in sorted(self.tasks_root.glob("*/task_*/task.json")):
|
| 126 |
with task_file.open("r", encoding="utf-8") as handle:
|
| 127 |
+
task_data = json.load(handle)
|
| 128 |
+
# Add task_id field for OpenEnv compatibility
|
| 129 |
+
task_num = task_data.get("id", 0)
|
| 130 |
+
task_data["task_id"] = f"task_{task_num}"
|
| 131 |
+
tasks.append(task_data)
|
| 132 |
return tasks
|
| 133 |
|
| 134 |
def state(self) -> dict[str, Any]:
|