Wuhp commited on
Commit
fd79c19
·
1 Parent(s): 482700b

Add preflight validation for selected community Nuclei templates

Browse files
Files changed (2) hide show
  1. app/main.py +22 -0
  2. tests/test_nuclei_check_api.py +28 -0
app/main.py CHANGED
@@ -116,6 +116,28 @@ async def nuclei_template_defaults():
116
  }
117
 
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  @app.get("/api/nuclei/templates", dependencies=[Depends(authorize)])
120
  async def search_nuclei_templates(q: str = "", page: int = 1):
121
  try:
 
116
  }
117
 
118
 
119
+ class NucleiTemplateCheckRequest(BaseModel):
120
+ paths: list[str] = Field(default_factory=list, max_length=config.NUCLEI_COMMUNITY_MAX)
121
+
122
+
123
+ @app.post("/api/nuclei/check", dependencies=[Depends(authorize)])
124
+ async def check_nuclei_community_templates(payload: NucleiTemplateCheckRequest):
125
+ if not payload.paths or len(payload.paths) != len(set(payload.paths)):
126
+ raise HTTPException(422, "Select distinct community template paths")
127
+ try:
128
+ nuclei_catalog.validate_selection([], payload.paths, [])
129
+ except ValueError as exc:
130
+ raise HTTPException(422, str(exc))
131
+ compatible, rejected = [], []
132
+ for path in payload.paths:
133
+ try:
134
+ await nuclei_catalog.community_files([path])
135
+ compatible.append(path)
136
+ except (ValueError, httpx.HTTPError) as exc:
137
+ rejected.append({"path": path, "reason": str(exc)[:180]})
138
+ return {"compatible": compatible, "rejected": rejected}
139
+
140
+
141
  @app.get("/api/nuclei/templates", dependencies=[Depends(authorize)])
142
  async def search_nuclei_templates(q: str = "", page: int = 1):
143
  try:
tests/test_nuclei_check_api.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from unittest.mock import patch
2
+ from fastapi.testclient import TestClient
3
+ from app import main
4
+
5
+
6
+ def test_nuclei_template_check_reports_supported_and_unsupported():
7
+ async def fake(paths):
8
+ if paths[0].endswith("bad.yaml"):
9
+ raise ValueError("Only GET/HEAD templates supported")
10
+ return {paths[0]: "safe"}
11
+ with patch.object(main.nuclei_catalog, "community_files", fake):
12
+ with TestClient(main.app) as client:
13
+ res=client.post("/api/nuclei/check",json={"paths":["http/web/a.yaml","http/web/bad.yaml"]})
14
+ assert res.status_code==200
15
+ assert res.json()["compatible"]==["http/web/a.yaml"]
16
+ assert len(res.json()["rejected"])==1
17
+
18
+
19
+ def test_nuclei_template_check_rejects_disallowed_repo_paths():
20
+ with TestClient(main.app) as client:
21
+ response=client.post("/api/nuclei/check",json={"paths":["http/../../etc/passwd"]})
22
+ assert response.status_code==422
23
+
24
+
25
+ def test_nuclei_template_check_rejects_duplicate_paths():
26
+ with TestClient(main.app) as client:
27
+ response=client.post("/api/nuclei/check",json={"paths":["http/a.yaml","http/a.yaml"]})
28
+ assert response.status_code==422