Metadata-Version: 2.4
Name: s2n-agent
Version: 0.1.0
Summary: LLM-powered decision layer for the S2N web vulnerability scanner
License: MIT
Project-URL: Homepage, https://github.com/s2n0n/s2n-agent
Project-URL: Repository, https://github.com/s2n0n/s2n-agent
Project-URL: Issues, https://github.com/s2n0n/s2n-agent/issues
Keywords: security,vulnerability-scanner,llm,agent,s2n
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Requires-Dist: click>=8.1
Provides-Extra: huggingface
Requires-Dist: transformers>=4.40; extra == "huggingface"
Requires-Dist: torch>=2.2; extra == "huggingface"
Requires-Dist: accelerate>=0.29; extra == "huggingface"
Requires-Dist: peft>=0.10; extra == "huggingface"
Provides-Extra: train
Requires-Dist: mlx-lm>=0.14; sys_platform == "darwin" and extra == "train"
Requires-Dist: datasets>=2.18; extra == "train"
Requires-Dist: trl>=0.8; extra == "train"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# S2N-Agent

> **LLM-powered decision layer for the [S2N](https://github.com/s2n0n/s2n) web vulnerability scanner**
>
> S2N 웹 취약점 스캐너를 위한 LLM 의사결정 레이어

---

## 개요 / Overview

**[한국어]**

S2N-Agent는 S2N 스캐너의 플러그인 선택·페이로드 계획·결과 해석을 LLM이 담당하도록 설계된 AI 레이어입니다.
기존 결정론적 스캔 파이프라인에 최소한의 변경으로 통합되며, Ollama(로컬)·HuggingFace(로컬 추론)·
Anthropic·OpenAI 중 사용자가 명시적으로 선택한 provider의 LLM으로 동작합니다 — 파인튜닝된 모델은
필요하지 않습니다.

```
기존: URL/DOM → 조건 충족 → Plugin 실행
목표: URL/DOM/SiteMap/응답 → S2N-Agent 추론 → Plugin 선택 + Payload 계획 → 결과 해석
```

**[English]**

S2N-Agent is an AI layer that delegates plugin selection, payload planning, and result interpretation to an LLM.
It integrates with the existing S2N scanner pipeline with minimal changes and runs on whichever provider you
explicitly select — Ollama (local), HuggingFace (local inference), Anthropic, or OpenAI. No fine-tuned model
is required.

```
Before: URL/DOM → rule match → Plugin execution
After:  URL/DOM/SiteMap/response → S2N-Agent reasoning → Plugin selection + Payload plan → Result interpretation
```

---

## 아키텍처: LLM은 어떻게 연동되는가 / Architecture: How the LLM Connects

**[한국어]**

에이전트는 provider 중립적으로 설계되어 있습니다. `LLMClient` 프로토콜(`s2nagent/client/base.py`)
뒤에 provider를 자유롭게 꽂아 넣는 구조이고, `build_client()`(`s2nagent/client/factory.py`)가
**사용자가 명시적으로 선택한** provider의 클라이언트를 생성합니다 — 학습이나 배포 없이 바로 사용
가능합니다:

| Provider | 무엇이 필요한가 |
| --- | --- |
| `ollama` | 로컬 Ollama 서버 + 아무 모델 태그 |
| `huggingface` | 로컬 GPU/MPS/CPU 추론 |
| `anthropic` | `ANTHROPIC_API_KEY` (또는 `--ai-api-key`) |
| `openai` | `OPENAI_API_KEY` 또는 OpenAI 호환 로컬 서버 `base_url` |

**provider는 반드시 명시해야 합니다** — 인자(`--provider`/`ai_provider`/`provider`) 또는
`S2NAGENT_PROVIDER` 환경변수 중 하나로 지정하지 않으면 Ollama나 HuggingFace로 자동 선택되지
않고 사용 가능한 provider 목록을 담은 오류로 즉시 종료됩니다. 에이전트를 실제로 동작시키는 데
필요한 최소 요건은 **위 표의 provider 중 하나(로컬이든 API든)와 그 provider를 명시적으로
선택하는 것뿐**입니다.

**[English]**

The agent is provider-neutral by design. Providers plug in behind the `LLMClient` protocol
(`s2nagent/client/base.py`), and `build_client()` (`s2nagent/client/factory.py`) constructs
whichever provider you **explicitly select** — no training or deployment required:

| Provider | What you need |
| --- | --- |
| `ollama` | A local Ollama server + any model tag |
| `huggingface` | Local GPU/MPS/CPU inference |
| `anthropic` | `ANTHROPIC_API_KEY` (or `--ai-api-key`) |
| `openai` | `OPENAI_API_KEY`, or an OpenAI-compatible local server `base_url` |

**Provider selection is mandatory** — specify it via an argument (`--provider`/`ai_provider`/
`provider`) or the `S2NAGENT_PROVIDER` environment variable. Omitting both raises a clear error
listing the available providers instead of silently picking Ollama or HuggingFace. The minimum to
actually run the agent is one provider from the table above, explicitly chosen.

---

## AI 모드 / AI Modes

| Mode         | 동작                                                          | Behavior                                                                |
| ------------ | --------------------------------------------------------------| ------------------------------------------------------------------------|
| `off`        | AI 없음, 기존 S2N 그대로                                     | Vanilla S2N, no AI                                                      |
| `assist`     | AI 권고만 로그 출력, 실행은 기존 방식                        | AI recommendations in log only, execution unchanged                    |
| `smart`      | 후보/payload 계획을 세우지만 실행 플러그인 목록은 변경하지 않음(실행 제어 미구현) | Plans candidates/payloads but does not change which plugins execute (execution control not yet implemented) |
| `aggressive` | confirmed finding마다 후속 payload를 로그로만 제안 — 공격 체인을 실행하지 않음 | Logs suggested follow-up payloads per confirmed finding — does not execute an attack chain |

---

## 설치 / Installation

> **현재 상태**: S2N-Agent는 PyPI에 배포되어 `pip install s2n-agent`로 설치할 수 있습니다.
> 다만 S2N(스캐너)은 `ai` extra가 포함된 버전이 아직 배포되지 않았으므로
> `pip install s2n[ai]`는 아직 동작하지 않습니다 — S2N 쪽은 아래 안내대로 `dev` 브랜치를 사용하세요.
>
> **Current status**: S2N-Agent is published to PyPI (`pip install s2n-agent`).
> S2N itself has not shipped a release containing the `ai` extra yet, so
> `pip install s2n[ai]` does not work yet — use S2N's `dev` branch as described below.

### 요구사항 / Requirements

- Python 3.10+
- [S2N](https://github.com/s2n0n/s2n) — 현재 `dev` 브랜치에 `--ai-mode` CLI 옵션이 포함되어 있습니다 (아직 정식 릴리스 태그 없음, 현재 `pyproject.toml` 버전은 `0.3.2`).
  S2N-Agent와 나란히 `code-projects/`(또는 원하는 부모 디렉터리)에 clone해두세요.
- LLM provider 하나를 선택해 준비: [Ollama](https://ollama.ai)(로컬) 또는 HuggingFace(로컬 추론) 또는
  Anthropic/OpenAI API 키. **자동 선택은 없으므로** `--ai-provider`로 반드시 명시해야 합니다.

### S2N-Agent 설치 (PyPI) / Install (PyPI)

S2N을 설치한 환경(venv)에 그대로 설치합니다.

```bash
pip install s2n-agent

# HuggingFace 로컬 추론까지 필요하면
pip install "s2n-agent[huggingface]"
```

### S2N-Agent 설치 (로컬 editable) / Install (local editable)

에이전트 자체를 수정하며 개발할 때 사용합니다.

```bash
# S2N과 나란히 clone
git clone https://github.com/s2n0n/s2n-agent.git
git clone https://github.com/s2n0n/s2n.git

# S2N 개발 환경(venv)에 S2N-Agent를 editable로 설치
cd s2n
python3 -m venv .venv && source .venv/bin/activate   # 또는 기존 개발 venv 사용
pip install -e ../s2n-agent

# HuggingFace 로컬 추론까지 필요하면
pip install -e "../s2n-agent[huggingface]"

# 개발(테스트) 환경
pip install -e "../s2n-agent[dev]"
```

설치 확인:

```bash
python3 -c "import s2nagent; print('OK')"
```

`ModuleNotFoundError`가 아니라 `OK`가 출력되면 준비 완료입니다 — S2N `scan --ai-mode ...`가 더 이상 "s2nagent 패키지 미설치" 경고 없이 AI 모드를 활성화합니다.

---

## S2N 통합 / S2N Integration

S2N `dev` 브랜치에 `--ai-mode`/`--ai-model`/`--ai-endpoint`/`--ai-provider`/`--ai-api-key` CLI 옵션이 내장되어 있습니다.
아직 정식 릴리스 태그는 없으므로, 위 [설치](#설치--installation) 절차대로 `dev` 브랜치를 사용하세요.

S2N's `dev` branch already includes built-in `--ai-mode`/`--ai-model`/`--ai-endpoint`/`--ai-provider`/`--ai-api-key` CLI options.
There's no tagged release yet, so follow the [Installation](#설치--installation) steps above to use the `dev` branch.

**두 개의 진입점 / Two entry points**: S2N에는 이 에이전트를 주입하는 경로가 두 곳입니다 — 둘 다
`s2n/s2nscanner/ai_integration.py`의 `build_ai_plugins()` 공용 헬퍼를 거쳐 동일한 `S2NAgentPlugin`을
동일한 방식으로 인스턴스화하므로, 어느 경로로 들어와도 에이전트 동작은 동일합니다.

1. **CLI** — 아래 [CLI 사용법](#cli-사용법--cli-usage) 참고.
2. **Chrome 확장 프로그램** — S2N의 `fix/on-scan-complete-hook` 브랜치(아직 `dev` 미병합)부터,
   `extension/`의 Options 페이지에서 AI 모드/provider/모델/엔드포인트/API 키를 설정하고 Popup의
   AI 토글을 켜면 Native Messaging(`native_host.py`)을 통해 동일한 에이전트가 주입됩니다.
   Popup 토글은 항상 `assist` 모드로 고정됩니다(README [AI 모드](#ai-모드--ai-modes) 표 기준 가장
   안전한 기본값). CLI 옵션에 없는 필드(예: 구버전 확장 프로그램)는 `ai_mode="off"`로 안전하게
   기본 처리되어 기존 스캔 동작에 영향이 없습니다.

There are two entry points that inject this agent into S2N — both go through the shared
`build_ai_plugins()` helper in `s2n/s2nscanner/ai_integration.py`, so behavior is identical
regardless of which path is used:

1. **CLI** — see [CLI Usage](#cli-사용법--cli-usage) below.
2. **Chrome extension** — as of S2N's `fix/on-scan-complete-hook` branch (not yet merged to `dev`),
   the extension's Options page lets you configure AI mode/provider/model/endpoint/API key, and the
   Popup's AI toggle (fixed to `assist` mode, the safest default) wires the same agent in via Native
   Messaging (`native_host.py`). Fields absent from older extension clients default safely to
   `ai_mode="off"`, so existing scans are unaffected.

### CLI 사용법 / CLI Usage

```bash
# AI 없음 (기존 동작) — provider 지정 불필요
s2n scan -u https://target.com

# assist 모드 — AI 권고를 로그에 출력, 실행은 기존 플러그인 (provider는 반드시 명시)
s2n scan -u https://target.com --ai-mode assist --ai-provider ollama

# smart 모드 — 후보/payload 계획 수립(실행 플러그인 목록은 아직 변경하지 않음)
s2n scan -u https://target.com --ai-mode smart --ai-provider ollama

# aggressive 모드 — confirmed finding마다 후속 payload를 로그로 제안(공격 체인 미실행)
s2n scan -u https://target.com --ai-mode aggressive --ai-provider ollama

# Ollama 모델/엔드포인트를 명시적으로 지정
s2n scan -u https://target.com \
  --ai-mode smart \
  --ai-provider ollama \
  --ai-model s2n-agent \
  --ai-endpoint http://localhost:11434

# Claude / GPT 등 API 기반 provider 사용
s2n scan -u https://target.com --ai-mode smart \
  --ai-provider anthropic --ai-model claude-sonnet-4-5 --ai-api-key "$ANTHROPIC_API_KEY"

s2n scan -u https://target.com --ai-mode smart \
  --ai-provider openai --ai-model gpt-4o-mini --ai-api-key "$OPENAI_API_KEY"
```

`--ai-provider`를 생략하면 `S2NAGENT_PROVIDER` 환경변수를 확인하고, 둘 다 없으면 `--ai-mode`가
`off`가 아닌 한 명확한 오류로 종료됩니다 — Ollama나 HuggingFace가 자동으로 선택되지 않습니다.
`--ai-api-key`를 생략하면 각 provider가 자체적으로 `ANTHROPIC_API_KEY`/`OPENAI_API_KEY` 환경변수를 읽습니다.

> **참고**: S2N CLI의 `--ai-provider` 자체 기본값은 아직 `None`입니다 — 이 저장소(`s2n-agent`)의
> provider 필수화는 S2N-Agent 쪽 라이브러리 동작이며, S2N CLI가 넘겨준 `ai_provider=None`을
> 그대로 받으면 위와 동일한 오류로 종료됩니다. 즉 `--ai-mode`를 `off`가 아닌 값으로 쓸 때는
> 항상 `--ai-provider`를 함께 지정하세요.

### Python API

```python
from s2nagent import S2NAgent

# provider는 반드시 명시해야 한다 — 생략하면 S2NAGENT_PROVIDER 환경변수를 확인하고,
# 둘 다 없으면 ValueError로 즉시 실패한다(Ollama/HuggingFace 자동 선택 없음).
agent = S2NAgent(provider="ollama", mode="smart")

# endpoint/model을 생략하면 선택된 provider 클라이언트 자신의 기본값을 쓴다 —
# 특정 Ollama 서버/모델 태그를 명시하고 싶을 때만 지정
agent = S2NAgent(provider="ollama", endpoint="http://localhost:11434", model="s2n-agent", mode="smart")

# Claude / GPT 등 다른 provider 사용 — endpoint를 생략하면 각 provider의 공식
# 엔드포인트(https://api.anthropic.com, https://api.openai.com/v1)를 쓴다
agent = S2NAgent(provider="anthropic", model="claude-sonnet-4-5", api_key="...")
agent = S2NAgent(provider="openai", model="gpt-4o-mini", api_key="...")
agent = S2NAgent(provider="openai", model="my-local-model",
                  endpoint="http://localhost:8000/v1")  # OpenAI 호환 로컬 서버

# Task A — 플러그인 선택
result = agent.select_plugin(
    url="/search?q=test",
    dom="<input name='q' type='text'>",
    sitemap_summary="3 forms, 0 file inputs",
)
# {"plugin": "xss", "confidence": 91, "reason": "input[name=q] detected"}

# Task B — 페이로드 계획
payloads = agent.plan_payloads(plugin="xss", parameter="q", context="html_body")
# {"payloads": ["<svg/onload=alert(1)>", ...], "strategy": "..."}

# Task C — FP 필터
verdict = agent.filter_false_positive(
    finding="Possible XSS",
    evidence="<script>alert(1)</script>",
    response_body="<script>alert(1)</script> reflected",
)
# {"verdict": "confirmed", "confidence": 95, "reason": "..."}

# Task D — 다음 액션 계획
plan = agent.plan_next_action(
    completed=["xss", "csrf"],
    findings=[{"plugin": "jwt", "severity": "HIGH"}],
    sitemap="admin route /admin/panel discovered",
)
# {"next_action": "path_traversal", "priority": "high", "reason": "..."}

# on_finding 콜백 (Scanner 실시간 피드백)
from s2n.s2nscanner.scan_engine import Scanner
scanner = Scanner(config=config, on_finding=agent.analyze_finding)
```

### Plugin Agent Registry

각 S2N 플러그인은 전담 `PluginAgent`를 가질 수 있습니다. `xss`/`sqlinjection`/`jwt`/`file_upload`(P0, 가장 정교한
전담 판정 로직)와 `csrf`/`oscommand`/`path_traversal`/`sensitive_files`/`brute_force`/`soft_brute_force`/`autobot`
(P1/P2, prompt profile 기반) 총 11개가 구현되어 있습니다. 모든 `PluginAgent`는 provider 선택과 무관하게 동일한
`LLMClient`를 공유합니다 — `registry.py`에 선언된 `adapter`/`fallback_model` 필드는 현재 메타데이터일 뿐 런타임
model 라우팅에는 쓰이지 않습니다. `react2shell`은 아직 registry에 `class: None`으로 남아있습니다 — 대응하는
S2N 플러그인 자체가 없는 상태(`docs/plugins/react2shell.md`)라 항상 `should_run=False`로 안전하게 no-op
처리됩니다.

```python
from s2nagent.plugin_agents import get_plugin_agent
from s2nagent.client import build_client

client = build_client(provider="anthropic", model="claude-sonnet-4-5", api_key="...")

agent = get_plugin_agent("brute_force", client)  # None이면 미구현 플러그인
decision = agent.evaluate_target(
    url="https://target.com/login",
    dom="<form><input type='password' name='pass'></form>",
    sitemap_summary="login form, no CAPTCHA observed",
)
# {"should_run": True, "confidence": 85, "reason": "...", "context": {"risk_signals": {...}}, ...}
```

각 에이전트는 LLM이 페이로드/판정을 "제안"하고, 파이썬 코드가 그 제안이 안전 범위를 벗어나지 않는지
결정론적으로 검증하는 구조를 공유합니다 — 예를 들어 재정렬 계열 메서드(`plan_payload_order` 등)는 LLM이
반환한 목록이 원래 후보 집합의 순열이 아니면 항상 원본 순서로 폴백하고, `brute_force`/`autobot`은 잠금정책·CAPTCHA·
WAF 챌린지가 감지되면 confidence와 무관하게 `should_run`을 강제로 `False`로 둡니다. 자세한 설계는
`docs/plugins/*.md`와 `docs/superpowers/specs/2026-08-02-p1-p2-plugin-agents-design.md`를 참고하세요.

---

## 모델 학습·배포 (실험적) / Model Training & Deployment (Experimental)

**[한국어]** 에이전트를 실행하는 데 파인튜닝된 모델은 필요하지 않습니다 — 위
[아키텍처](#아키텍처-llm은-어떻게-연동되는가--architecture-how-the-llm-connects) 섹션의 provider
중 하나만 명시적으로 선택하면 바로 동작합니다.

`scripts/train.py`/`evaluate.py`/`deploy_ollama.sh`, `configs/lora_*.yaml`, `data/*.jsonl`,
`s2nagent/models/Modelfile`은 XSS `PluginAgent` 판정 정확도를 높이려는 선택적 실험 자산입니다.
현재 다음 사유로 에이전트의 핵심 실행 경로에는 연결되어 있지 않습니다:

- `plugin_agents/registry.py`의 `adapter`/`fallback_model` 필드가 런타임 model 라우팅에 쓰이지 않음
  (모든 `PluginAgent`가 provider 선택과 무관하게 동일한 client를 공유).
- 학습 파이프라인이 두 갈래(`scripts/train.py`의 MLX 기반과 `scripts/plugin_agents/train_xss_lora_peft.py`의
  HF PEFT 기반)로 나뉘어 있고, `deploy_ollama.sh`가 기대하는 MLX `.npz` adapter 포맷과 현재 커밋된
  adapter(`adapters/*/adapter_model.safetensors`, HF PEFT 형식)가 서로 호환되지 않음.
- 가장 긍정적인 평가 결과(HF+PEFT, +26%p)가 재현 가능한 형태로 커밋되어 있지 않음.

각 스크립트를 직접 실험해보고 싶다면 `--help`를 참고하세요. 저장소를 정리하는 방향은
`docs/agent-development-plan.md` §1.2(제외 범위)를 참고하세요.

**[English]** Fine-tuning is not required to run the agent — any provider from the Architecture
section above works out of the box once explicitly selected.

`scripts/train.py`/`evaluate.py`/`deploy_ollama.sh`, `configs/lora_*.yaml`, `data/*.jsonl`, and
`s2nagent/models/Modelfile` are optional experiments aimed at improving the XSS `PluginAgent`'s
judgment accuracy. They are not wired into the agent's core execution path: the registry's
`adapter`/`fallback_model` fields aren't used for runtime model routing, the training pipeline is
split across two incompatible formats (MLX `.npz` vs. the committed HF PEFT
`adapter_model.safetensors`), and the most positive evaluation result isn't committed in
reproducible form. See each script's `--help` if you want to experiment, and
`docs/agent-development-plan.md` §1.2 for the scoping rationale.

---

## 프로젝트 구조 / Project Structure

```
s2n-agent/
├── s2nagent/
│   ├── agent.py                  # S2NAgent — 최상위 오케스트레이터
│   ├── cli.py                    # `s2n-agent` 독립 CLI (select/plan/filter/next/deploy/smoke)
│   ├── constants.py               # PLUGINS 목록, AGENT_MIN_CONFIDENCE 등 전역 상수
│   ├── client/
│   │   ├── base.py               # LLMClient Protocol — 모든 provider가 만족해야 하는 계약
│   │   ├── factory.py            # build_client() — provider 명시 필수(ollama/hf/anthropic/openai)
│   │   ├── ollama.py             # Ollama /api/generate 클라이언트
│   │   ├── huggingface.py        # HuggingFace 로컬 추론 (MPS/CUDA/CPU)
│   │   ├── anthropic_client.py   # Claude API 클라이언트
│   │   └── openai_compatible.py  # OpenAI/OpenAI 호환 서버 클라이언트
│   ├── plugin_agents/            # 플러그인별 전담 에이전트 (registry.py가 이름→클래스 매핑)
│   │   ├── base.py                # BasePluginAgent, estimate_os_from_context 등 공유 헬퍼
│   │   ├── registry.py            # get_plugin_agent(name, client) — 11개 구현 + react2shell(class=None)
│   │   ├── xss.py, sqlinjection.py, jwt.py, file_upload.py       # P0 (가장 정교한 전담 로직)
│   │   └── csrf.py, oscommand.py, path_traversal.py,             # P1/P2 (공통 모델 + prompt profile)
│   │       sensitive_files.py, brute_force.py, soft_brute_force.py, autobot.py
│   ├── plugins/
│   │   └── s2n_agent_plugin.py   # S2NAgentPlugin (pre_scan/run/post_scan/cleanup/on_scan_complete)
│   ├── tasks/
│   │   ├── plugin_selection.py   # Task A
│   │   ├── router.py              # 상위 top-k 후보 선정 (pre_scan에서 사용)
│   │   ├── payload_planning.py   # Task B (자유 생성 모드 + 후보 재정렬 모드)
│   │   ├── false_positive.py     # Task C
│   │   └── multi_step.py         # Task D
│   ├── data/
│   │   ├── generator.py          # 학습 데이터 생성기
│   │   └── schemas.py            # ChatML 스키마
│   └── models/
│       └── Modelfile             # Ollama 배포 설정
├── configs/
│   ├── lora_3b.yaml              # Qwen2.5-Coder-3B LoRA 설정
│   └── lora_7b.yaml              # Qwen2.5-Coder-7B LoRA 설정
├── data/
│   ├── train.jsonl               # 학습 데이터 (3,200 samples)
│   ├── valid.jsonl               # 검증 데이터 (400 samples)
│   └── test.jsonl                # 테스트 데이터 (400 samples)
├── docs/
│   ├── plugins/                  # 플러그인별 전담 에이전트 설계서 (16섹션 구조) + integration-gaps.md
│   └── superpowers/               # 이 저장소에서 진행한 spec/plan 문서 아카이브
├── scripts/
│   ├── split_data.py             # train/val/test 분리
│   ├── train.py                  # mlx-lm 학습 래퍼
│   ├── evaluate.py               # 평가 스크립트
│   └── deploy_ollama.sh          # Ollama 배포 자동화
├── tests/                        # pytest — plugin_agents/tasks/registry 전체 커버
└── pyproject.toml
```

---

## S2N 통합 상세 / S2N Integration Details

S2N 스캐너의 플러그인 생명주기 훅을 활용합니다. CLI(`runner.py`)와 Chrome 확장 프로그램
(`native_host.py`) 양쪽 진입점 모두 이 훅 순서를 그대로 따릅니다 — `S2NAgentPlugin`은
`Scanner.plugins` 리스트의 맨 앞에 놓여 항상 다른 플러그인보다 먼저 실행됩니다.

Hooks into S2N scanner's plugin lifecycle. Both entry points (the CLI's `runner.py` and the Chrome
extension's `native_host.py`) follow the exact same hook sequence — `S2NAgentPlugin` is always
prepended to `Scanner.plugins`, so it runs before every other plugin.

```
Scanner.scan()
  ↓ smart_crawl() → scan_context.sitemap 자동 첨부
  ↓ for plugin in discovered_plugins:
      ↓ plugin.pre_scan(ctx)   ← AI: sitemap 분석, 실행 여부 결정
      ↓ plugin.run(ctx)        ← 실제 스캔
      ↓ plugin.post_scan(ctx)  ← AI: 결과 해석, 다음 액션 계획
      ↓ plugin.cleanup(ctx)
  ↓ for plugin in discovered_plugins:      ← 전체 루프 종료 후 1회
      ↓ plugin.on_scan_complete(ctx, results)  ← AI: aggregate FP 필터 + multi-step planner
  ↓ ScanReport 반환
```

> `on_scan_complete`는 개별 플러그인이 아니라 스캔 전체 결과를 종합해야 하는 훅이라 루프 밖에서 한 번만 호출됩니다.
> S2N `dev` 브랜치에 이 호출부가 배선되어 있습니다 ([s2n0n/s2n#154](https://github.com/s2n0n/s2n/pull/154)).

**각 훅에서 에이전트가 실제로 하는 일 / What the agent actually does at each hook**

| 훅 / Hook | 에이전트 역할 / Agent's role | 담당 Task |
| --- | --- | --- |
| `pre_scan` | RouterTask로 sitemap 기반 top-k 플러그인 후보 선정 → (smart/aggressive) P0 PluginAgent로 후보별 `should_run` 평가 → 최적 후보에 payload 계획 | A (선택), B (페이로드) |
| `run` | (`assist` 모드만) 위에서 나온 권고를 로그로만 출력 — 실제 실행은 여전히 기존 플러그인들이 담당 | — |
| `on_finding` (실시간 콜백) | 각 finding이 리포팅되는 즉시 FP 판정 + (`aggressive`) confirmed된 항목에 후속 payload 권고 | C (FP 필터) |
| `post_scan` | 에이전트 자신의 `PluginResult` 반환(집계는 `on_scan_complete`로 이관) | — |
| `on_scan_complete` (전체 스캔 종료 후 1회) | 실시간/일괄 finding 병합 → 최종 FP 필터 → "다음에 뭘 스캔해야 하는가" 계획 → `session_data["agent_state"]`에 저장 | C (집계 FP 필터), D (다음 액션) |

즉 에이전트는 스캔 결과를 만들어내는 주체가 아니라, **기존 11개 플러그인을 언제/어떤 순서로 돌릴지
결정하고, 그 결과를 해석해서 다음 액션을 제안하는 의사결정 레이어**입니다 — `off` 모드에서는 이
모든 훅이 즉시 no-op으로 반환되어 기존 S2N과 100% 동일하게 동작합니다.

In other words, the agent doesn't produce scan results itself — it's a **decision layer that decides
when/in what order to run the existing 11 plugins, and interprets their results to propose the next
action.** With `ai_mode="off"`, every one of these hooks returns immediately as a no-op, so behavior
is identical to vanilla S2N.

**수동 통합 / Manual integration (without CLI):**

```python
from s2n.s2nscanner.scan_engine import Scanner
from s2n.s2nscanner.interfaces import ScanConfig, ScannerConfig
from s2nagent.plugins.s2n_agent_plugin import S2NAgentPlugin

agent_plugin = S2NAgentPlugin(
    ai_mode="smart",
    ai_provider="ollama",  # 반드시 명시 — 생략하면 ValueError (자동 선택 없음)
    # ai_model/ai_endpoint를 생략하면 선택한 provider의 기본값을 쓴다.
    # ai_provider="anthropic", ai_model="claude-sonnet-4-5", ai_api_key="...",  # Claude/GPT 등 사용 시
)

scanner = Scanner(
    config=ScanConfig(
        target_url="https://target.com",
        scanner_config=ScannerConfig(ai_mode="smart"),
    ),
    plugins=[agent_plugin],
    on_finding=lambda f: print(f"Finding: {f.title}"),
)
report = scanner.scan()
```

---

## MITRE ATT&CK 매핑 / MITRE ATT&CK Mapping

S2N-Agent가 선택할 수 있는 플러그인과 ATT&CK 매핑.
Plugins available for AI selection and their ATT&CK mappings.

| Plugin              | TID       | Tactic            | Agent 구현 |
| ------------------- | --------- | ----------------- | --------- |
| `xss`               | T1059.007 | Execution         | ✅ P0 |
| `sqlinjection`      | T1190     | Initial Access    | ✅ P0 |
| `jwt`               | T1528     | Credential Access | ✅ P0 |
| `file_upload`       | T1505.003 | Persistence       | ✅ P0 |
| `oscommand`         | T1059     | Execution         | ✅ P1 |
| `path_traversal`    | T1083     | Discovery         | ✅ P1 |
| `sensitive_files`   | T1552.001 | Credential Access | ✅ P1 |
| `csrf`              | T1185     | Collection        | ✅ P2 |
| `brute_force`       | T1110     | Credential Access | ✅ P2 |
| `soft_brute_force`  | T1110     | Credential Access | ✅ P2 |
| `autobot`           | T1190     | Initial Access    | ✅ P2 |
| `react2shell`       | T1505.003 | Persistence       | ⛔️ 미구현 — 대응하는 S2N 플러그인 없음 (`docs/plugins/react2shell.md`), registry에서 항상 `should_run=False` |

---

## 개발 로드맵 / Development Roadmap

| 단계                       | 상태    | 작업                                                                                                                                        |
| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Week 1                     | ✅ 완료 | ScannerConfig AI 필드, CLI 옵션, 패키지 구조, Ollama/HF 클라이언트, Tasks A-D, 4,000 샘플 생성                                              |
| Week 2                     | ✅ 완료 | train/val/test 분리, LoRA 설정(3B/7B), 학습 스크립트, 평가 스크립트, Ollama 배포 자동화                                                     |
| Week 3                     | ✅ 완료 | `on_scan_complete` 훅 구현(집계 FP 필터/multi-step planner), SiteMap 통합, P0 4개 에이전트(xss/sqli/jwt/file_upload)                        |
| P1/P2 플러그인 에이전트    | ✅ 완료 | csrf/oscommand/path_traversal/sensitive_files/brute_force/soft_brute_force/autobot 7종 구현 — `docs/plugins/*.md` 설계 기반, registry 배선 완료 |
| Multi-provider LLM client  | ✅ 완료 | `build_client()` 팩토리로 Ollama/HuggingFace/Anthropic(Claude)/OpenAI(호환) provider 명시적 선택 지원                                       |
| S2N 쪽 크로스레포 배선     | ✅ 완료 | `on_scan_complete` 호출부 S2N `scan_engine.py`에 추가 ([s2n0n/s2n#154](https://github.com/s2n0n/s2n/pull/154)), `pyproject.toml` build-backend 버그 수정 |
| 확장 프로그램 AI 모드 연동 | ✅ 완료 | S2N `fix/on-scan-complete-hook` 브랜치(미병합)에 `ai_integration.build_ai_plugins()` 공용 헬퍼 + `native_host.py` 배선, Native Messaging 프로토콜에 `ai_*` 필드 추가, 확장 프로그램 Popup/Options에 AI 토글·설정 UI 추가 — CLI와 동일한 에이전트가 이제 브라우저 확장에서도 동작 |
| 플러그인 registry 정합성   | ✅ 완료 | `s2nagent.constants.get_known_plugins()`가 s2n 설치 시 실제 `discover_plugins()`와 대조해 드리프트(`react2shell` 등)를 1회 경고로 감지 |
| Stage 1: 현재 동작 정합화  | ✅ 완료 | provider별 기본 endpoint/model 격리(Anthropic/OpenAI가 CLI 포함 어디서도 더 이상 Ollama 주소를 받지 않음), `assist` 모드가 아무도 쓰지 않던 `plugin_recommendation` 대신 실제로 채워지는 `router_candidates`를 읽도록 수정, `on_scan_complete`의 `completed_plugins`에서 agent 자신 제외 — `docs/agent-development-plan.md` §12 1단계 완료 조건 충족 (180 tests) |
| provider 필수 선택         | ✅ 완료 | `build_client()`의 `auto`(Ollama 우선 탐지 → HuggingFace 폴백) 기본 동작 제거 — provider를 인자 또는 `S2NAGENT_PROVIDER` 환경변수로 명시하지 않으면 명확한 오류로 종료. CLI `--provider` choice에서도 `auto` 제거, S2N CLI를 통한 사용도 이제 `--ai-provider` 명시 필요 |
| Stage 2–7 (shadow planner, PolicyValidator, 전역 request budget, bounded executor 등) | 🔄 예정 | 실행 제어를 실제로 구현하는 남은 단계 — 설계와 완료 조건은 `docs/agent-development-plan.md` §5–§12 참고. S2N(스캐너) 저장소 변경이 필요해 별도 계획으로 진행 |
| react2shell                | ⏳ 보류 | S2N 저장소에 대응하는 React SSR/템플릿 인젝션 플러그인 자체가 없음 — registry는 항상 `should_run=False`로 안전하게 no-op 처리 (`docs/plugins/react2shell.md`) |
| P1 LoRA 어댑터 승격        | ⏸️ 보류 | `docs/agent-development-plan.md` §1.2에 따라 파인튜닝/adapter 작업은 이번 로드맵 범위에서 제외 — 실행 제어(Stage 2–7)와 end-to-end 벤치마크 완료 후, 재현 가능한 10%p+ 개선이 확인될 때만 재개 |
| 학습 데이터셋 확충         | ⏸️ 보류 | 위와 동일한 사유로 보류 — `data/`·`scripts/train.py` 등은 실험 자산으로 유지되며 핵심 실행 경로에 연결하지 않음 |
| PyPI 배포 (S2N-Agent)      | ✅ 완료 | `v0.1.0` 태그 push → GitHub Actions가 Trusted Publishing(OIDC)으로 PyPI 배포 — `pip install s2n-agent` 가능                                  |
| PyPI 배포 (S2N 연동)       | 🔄 예정 | S2N PyPI 최신 릴리스(`0.3.2`)에는 `ai` extra가 없음 — S2N 쪽 버전 승격·재배포가 있어야 `pip install s2n[ai]`가 동작. `ai` extra에 상한(`>=0.1.0,<0.2`)도 함께 필요 |
| CI AI 모드 통합 테스트     | ⏳ 예정 | S2N CI에 `--ai-mode assist` 스모크 테스트 + registry/`discover_plugins()` 정합성 단위 테스트 추가                                           |

---

## 라이선스 / License

MIT License. See [LICENSE](LICENSE).

---

> **주의**: 이 도구는 허가된 보안 테스트 및 교육 목적으로만 사용하세요.
> **Warning**: This tool is intended for authorized security testing and educational purposes only.
