CoolFace
Apppublic

KSingh08/soc2-auditor

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
App README

SOC 2 Evidence Auditor

An OpenEnv reinforcement learning environment where an LLM agent acts as a SOC 2 security auditor.

The agent inspects mock evidence files (JSON logs from AWS, GitHub, HR systems) and makes deterministic APPROVE or REJECT decisions against stated security control requirements — exactly as a real compliance auditor would.

Why This Environment Matters

SOC 2 compliance is mandatory for any SaaS company handling customer data. Auditors review hundreds of evidence items per audit cycle, each requiring careful cross-referencing of logs, timestamps, and policy documents. This is tedious, error-prone work that AI agents could meaningfully assist with — but requires precise, rule-following behavior with zero hallucination tolerance.

Quick Start

python
import asyncio
from soc2 import SOC2Env, SOC2Action

async def main():
    env = await SOC2Env.from_docker_image("soc2-auditor:latest")
    try:
        # Reset to a specific task
        result = await env.reset(task_id="pr_approval_check")
        obs = result.observation
        print(f"Task: {obs.task_id}")
        print(f"Control: {obs.control_requirement}")
        print(f"Files: {obs.available_files}")

        # Inspect evidence
        result = await env.step(SOC2Action(
            type="INSPECT_FILE",
            file_name="pull_request_log.json"
        ))
        print(f"Reward: {result.reward}")  # +0.1

        # Submit decision
        result = await env.step(SOC2Action(
            type="SUBMIT_DECISION",
            decision="REJECT",
            reason="MISSING_APPROVAL"
        ))
        print(f"Final reward: {result.reward}")  # +0.9
        print(f"Done: {result.done}")            # True
    finally:
        await env.close()

asyncio.run(main())

Action Space

The agent has exactly 3 action types:

ActionFieldsDescription
INSPECT_FILEfile_name: strRead an evidence file. Adds content to inspected_files. Cannot be used on large log files.
SEARCH_LOGSfile_name: str, query_field: str, query_value: strQuery a large log file by filtering events where query_field == query_value.
SUBMIT_DECISION`decision: APPROVE\REJECT, reason: str`Render the audit verdict. Ends the episode.

Reason codes for SUBMIT_DECISION:

CodeWhen to use
MISSING_APPROVALRequired sign-off, approval, or authorization is absent
SLA_VIOLATIONA time-based deadline or requirement was exceeded
MISSING_TIMESTAMPA system-generated timestamp is required but missing
INCOMPLETE_REVOCATIONAccess not fully revoked across all required systems or credentials
NONEUse only when approving (no violation found)

Observation Space

FieldTypeDescription
task_idstrCurrent task identifier
control_requirementstrThe security rule to enforce
available_filesList[str]Evidence files available to inspect or search
inspected_filesDict[str, Any]Contents of files inspected (INSPECTFILE) and search results (SEARCHLOGS)
audit_statusstrIN_PROGRESS / APPROVED / REJECTED
step_rewardfloatReward earned in the last step (delta only)
cumulative_rewardfloatTotal episode reward so far
messagestrFeedback about the last action
doneboolTrue after SUBMIT_DECISION

Reward Function

The reward function provides partial progress signals throughout the episode. Maximum per episode is 1.0.

ActionConditionReward
INSPECT_FILEFirst inspection of a relevant file (max 3 = 0.3 total)+0.1
INSPECT_FILERe-inspection of same file0.0
INSPECT_FILEDistractor file-0.05
INSPECT_FILELarge log file (use SEARCH_LOGS)-0.05
INSPECT_FILEFile not in available_files-0.1
SEARCH_LOGSProductive search of relevant log file+0.1 (shares 0.3 cap)
SEARCH_LOGSEmpty results on relevant file-0.05
SEARCH_LOGSDistractor or non-large file-0.05
SEARCH_LOGSRepeated identical query0.0
SUBMIT_DECISIONCorrect decision + correct reason+(1.0 − inspect_reward_earned)
SUBMIT_DECISIONCorrect decision + wrong reason+0.2
SUBMIT_DECISIONAPPROVE on non-compliant evidence-0.5
SUBMIT_DECISIONREJECT on compliant evidence-0.3

Maximum per episode: 0.3 (inspect cap) + 0.7 (correct submit) = 1.0

The +(1.0 − inspect_reward_earned) formula guarantees the episode total is exactly 1.0 regardless of how many relevant files were inspected before submitting.

The 3 Graded Tasks

Task 1: PR Approval Check (Easy)

Control: All code changes to production branches require peer review approval before merging.

Evidence: pull_request_log.json — a GitHub PR with approved_by: null and approvals_count: 0.

Correct answer: REJECT / MISSING_APPROVAL

Why it's easy: Single file, obvious null field, direct rule match. Agent must ignore 5 distractors.


Task 2: Access Revocation SLA (Medium)

Control: User access must be revoked within 24 hours of employee termination.

Evidence:

  • —hr_termination_ticket.json — termination recorded at 2024-10-01T09:00:00Z
  • —aws_iam_audit_log.json — access removed at 2024-10-05T11:30:00Z (98.5 hours later)

Correct answer: REJECT / SLA_VIOLATION

Why it's medium: Requires inspecting both files and computing the time delta (98.5h > 24h SLA).


Task 3: Multi-System Access Revocation (Hard)

Control: Upon termination, access to ALL production systems — AWS, GitHub, and Production DB — must be revoked.

Evidence:

  • —hr_terminations.json — alice_dev terminated 2024-10-01T17:00:00Z
  • —aws_users.json — alice_dev removed ✓
  • —github_users.json — alice_dev removed ✓
  • —prod_db_users.json — alice_dev still active ✗

Correct answer: REJECT / INCOMPLETE_REVOCATION

Why it's hard: Agent must inspect all 4 system files. AWS and GitHub show proper revocation — only the prod DB reveals the violation. Rushing to REJECT after 2 files would get the reason wrong; approving after seeing 2 good files would miss the DB failure.


Extended Task Pool (11 Total Controls)

The environment includes 11 audit controls total. When reset() is called without a task_id, a random task is selected:

Task IDControlDifficulty
pr_approval_checkCode review approvalEasy
access_revocation_sla24-hour access revocationMedium
multi_system_access_revocationFull multi-system revocationHard
mfa_enforcement_checkMFA on all console loginsEasy
encryption_at_rest_checkS3 encryption requiredEasy
incident_response_sla4-hour escalation SLAMedium
vendor_security_reviewVendor questionnaire on fileMedium
access_review_timestampSystem timestamp required (hallucination trap)Hard
access_review_compliantSystem timestamp present — APPROVE trapMedium
change_management_approvalCAB approval for HIGH-risk changesMedium
password_policy_compliance100% password policy complianceMedium
cloudtrail_privileged_access_auditAPI key revocation via SEARCH_LOGSHard

SEARCH_LOGS Task: CloudTrail Privileged Access Audit

Control: All AWS credentials (API keys + tokens) must be fully revoked within 24 hours of termination.

Evidence:

  • —hr_terminations.json — alice_dev terminated 2024-10-01T17:00:00Z
  • —aws_cloudtrail_full_log.json — 28 API events (too large to INSPECT_FILE directly)

Workflow:

  1. 1.INSPECT_FILE → hr_terminations.json → extract username alice_dev
  2. 2.SEARCH_LOGS → aws_cloudtrail_full_log.json, query_field=username, query_value=alice_dev
  3. 3.Results show 3 API calls (CT-011, CT-014, CT-017) made 14–41 hours after termination
  4. 4.SUBMIT_DECISION → REJECT / INCOMPLETE_REVOCATION

Why it's hard: Multi-hop reasoning — must extract a value from one file and use it as a query parameter for another. Tests whether agents can chain observations across steps.

Setup & Usage

Build Docker Image

bash
docker build -t soc2-auditor:latest .

Run Server Locally

bash
# Via uv
uv run server

# Via Docker
docker run -p 8000:8000 soc2-auditor:latest

Run Baseline Inference

bash
# Set environment variables
export API_KEY=your_token_here
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
export IMAGE_NAME=soc2-auditor:latest

uv run python inference.py

Environment Variables

VariableRequiredDefaultDescription
API_KEYYes—HuggingFace API token
API_BASE_URLNohttps://router.huggingface.co/v1LLM endpoint
MODEL_NAMENoQwen/Qwen2.5-72B-InstructModel identifier
IMAGE_NAMENosoc2-auditor:latestDocker image name

OpenEnv Validate

bash
openenv validate

Project Structure

soc2_auditor/
├── inference.py                        # Baseline inference script (entry point)
├── models.py                           # SOC2Action + SOC2Observation Pydantic models
├── client.py                           # SOC2Env client (WebSocket + Docker)
├── __init__.py                         # Package exports
├── openenv.yaml                        # OpenEnv spec manifest
├── pyproject.toml                      # Project metadata + dependencies
├── Dockerfile                          # Container definition
├── .env                                # Local credentials (not committed)
└── server/
    ├── app.py                          # FastAPI application
    ├── soc2_environment.py             # SOC2Environment core logic
    ├── tasks.py                        # Task definitions, evidence data, grader
    └── __init__.py

Baseline Scores

Scores from Qwen/Qwen2.5-72B-Instruct via HuggingFace Inference API:

TaskDifficultyExpected Agent Score
pr_approval_checkEasy0.85–1.0
access_revocation_slaMedium0.5–0.85
multi_system_access_revocationHard0.2–0.5
Overall—~0.55

The hard task requires inspecting all 4 system files and choosing INCOMPLETE_REVOCATION over MISSING_APPROVAL — a distinction many models miss without careful reasoning.