websec-validator 0.2.5__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {websec_validator-0.2.5/src/websec_validator.egg-info → websec_validator-0.2.6}/PKG-INFO +1 -1
- {websec_validator-0.2.5 → websec_validator-0.2.6}/pyproject.toml +1 -1
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/findings.py +3 -1
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/scanners.py +25 -3
- {websec_validator-0.2.5 → websec_validator-0.2.6/src/websec_validator.egg-info}/PKG-INFO +1 -1
- {websec_validator-0.2.5 → websec_validator-0.2.6}/tests/test_hardening.py +36 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/LICENSE +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/README.md +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/setup.cfg +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/__init__.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/briefing.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/calibration.json +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/calibration.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/cli.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/constitution.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/corpus.json +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/dynamic.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/__init__.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/auth.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/authz.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/base.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/client_exposure.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/graphql.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/iac_ci.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/integrations.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/routes.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/schemas.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/stack.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/surface.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/tenant.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/probes.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/proof.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/recon.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/report.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/_lib.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/bola-cross-tenant.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/bola-write-verbs.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/compare-roles.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/dlp-bypass-offline.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/forged-token.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/hs256-brute-force.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/jwt-attacks.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/mass-assignment.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/race-conditions.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/rate-limit-burst.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/s3-assess.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/ssrf-probes.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/unauth-baseline.sh +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/webhook-forgery.py +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/reports/FINDINGS-SUMMARY.md.template +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/reports/access-control-matrix.md.template +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/reports/findings-triage.md.template +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/reports/pentest-handover-brief.md.template +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/reports/per-tool-FINDINGS.md.template +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/SOURCES.txt +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/dependency_links.txt +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/entry_points.txt +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/top_level.txt +0 -0
- {websec_validator-0.2.5 → websec_validator-0.2.6}/tests/test_recon.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: websec-validator
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Local-first security recon that briefs your AI coding agent: facts + tailored probe scripts, code-in / artifacts-out. No LLM, no server, no running app.
|
|
5
5
|
Author: Ricardo Accioly
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "websec-validator"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.6"
|
|
8
8
|
description = "Local-first security recon that briefs your AI coding agent: facts + tailored probe scripts, code-in / artifacts-out. No LLM, no server, no running app."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -184,7 +184,9 @@ def build_ledger(facts: dict, unified: dict | None, dynamic: dict | None = None,
|
|
|
184
184
|
cat = t.get("category", "")
|
|
185
185
|
cls = cat_to_class.get(cat, "sast")
|
|
186
186
|
sev = t.get("severity", "MEDIUM")
|
|
187
|
-
|
|
187
|
+
# Confidence follows severity for secrets/CVEs: a generic-api-key tiered down to MEDIUM
|
|
188
|
+
# (low-precision rule, bug-072) should NOT be stamped HIGH-confidence — keep P(real) honest.
|
|
189
|
+
conf = "HIGH" if (cat in ("secret", "sca") and sev in ("HIGH", "CRITICAL")) else "MEDIUM"
|
|
188
190
|
out.append(_f(t.get("title", cat), f"static-{cat}", cls, sev, conf, t.get("file", ""),
|
|
189
191
|
[{"layer": "static", "detail": f"{'+'.join(t.get('tools', []))}: {t.get('title','')}"}]))
|
|
190
192
|
|
|
@@ -200,6 +200,23 @@ def _aws_secret_tier(secret: str, match: str):
|
|
|
200
200
|
return None, None
|
|
201
201
|
|
|
202
202
|
|
|
203
|
+
# gitleaks/trivy "generic" + entropy/keyword rules are high-recall, low-precision: they fire on
|
|
204
|
+
# public keys, wallet addresses, hashes, env-var refs and test fixtures about as often as real
|
|
205
|
+
# credentials. Tier those to MEDIUM + a verify note (NEVER hide them) so the HIGH secret tier
|
|
206
|
+
# stays trustworthy in a shareable report; specific-format rules (AKIA, private-key, GitHub/
|
|
207
|
+
# Stripe/Slack/JWT, etc.) keep HIGH. (bug-072 — dogfooding a wallet app surfaced ~20 HIGH
|
|
208
|
+
# generic-api-key FPs in committed source.)
|
|
209
|
+
_GENERIC_SECRET_RULES = {"generic-api-key", "generic-api-key-1", "generic", "api-key",
|
|
210
|
+
"secret-keyword", "high-entropy", "high-entropy-string", "entropy"}
|
|
211
|
+
_GENERIC_NOTE = ("generic/entropy match — verify it's a live credential "
|
|
212
|
+
"(often a public key, address, hash or env-ref, not a secret)")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _generic_secret(rule: str) -> bool:
|
|
216
|
+
r = (rule or "").lower()
|
|
217
|
+
return r in _GENERIC_SECRET_RULES or "generic" in r or "entropy" in r
|
|
218
|
+
|
|
219
|
+
|
|
203
220
|
def _norm_trivy(data: dict) -> list:
|
|
204
221
|
out = []
|
|
205
222
|
for res in (data.get("Results") or []):
|
|
@@ -210,11 +227,14 @@ def _norm_trivy(data: dict) -> list:
|
|
|
210
227
|
"title": f"{v.get('PkgName')} {v.get('InstalledVersion')} → {v.get('FixedVersion', '(no fix)')}",
|
|
211
228
|
"fingerprint": f"cve|{v.get('PkgName')}|{v.get('VulnerabilityID')}"})
|
|
212
229
|
for s in (res.get("Secrets") or []):
|
|
230
|
+
rid = s.get("RuleID", "")
|
|
213
231
|
sev, note = _aws_secret_tier(s.get("Match", ""), s.get("Code", "") or "")
|
|
214
|
-
|
|
232
|
+
if not sev and _generic_secret(rid):
|
|
233
|
+
sev, note = "MEDIUM", _GENERIC_NOTE
|
|
234
|
+
title = f"secret: {s.get('Title') or rid}" + (f" — {note}" if note else "")
|
|
215
235
|
out.append({"tool": "trivy", "category": "secret", "severity": sev or _sev(s.get("Severity") or "HIGH"),
|
|
216
|
-
"key":
|
|
217
|
-
"title": title, "fingerprint": f"secret|{tgt}|{
|
|
236
|
+
"key": rid, "file": tgt, "line": s.get("StartLine", 0),
|
|
237
|
+
"title": title, "fingerprint": f"secret|{tgt}|{rid}"})
|
|
218
238
|
for m in (res.get("Misconfigurations") or []):
|
|
219
239
|
out.append({"tool": "trivy", "category": "iac", "severity": _sev(m.get("Severity")),
|
|
220
240
|
"key": m.get("ID", ""), "file": tgt, "line": 0, "title": (m.get("Title") or "")[:90],
|
|
@@ -228,6 +248,8 @@ def _norm_gitleaks(data) -> list:
|
|
|
228
248
|
for x in rows:
|
|
229
249
|
f, rule = x.get("File", ""), x.get("RuleID", "")
|
|
230
250
|
sev, note = _aws_secret_tier(x.get("Secret", ""), x.get("Match", ""))
|
|
251
|
+
if not sev and _generic_secret(rule):
|
|
252
|
+
sev, note = "MEDIUM", _GENERIC_NOTE
|
|
231
253
|
title = f"secret: {(x.get('Description') or rule)[:80]}" + (f" — {note}" if note else "")
|
|
232
254
|
out.append({"tool": "gitleaks", "category": "secret", "severity": sev or "HIGH",
|
|
233
255
|
"key": rule, "file": f, "line": x.get("StartLine", 0),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: websec-validator
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Local-first security recon that briefs your AI coding agent: facts + tailored probe scripts, code-in / artifacts-out. No LLM, no server, no running app.
|
|
5
5
|
Author: Ricardo Accioly
|
|
6
6
|
License: MIT
|
|
@@ -133,5 +133,41 @@ class ProbeRegistrationTests(unittest.TestCase):
|
|
|
133
133
|
self.assertEqual(ctx["endpoints"]["reads"], ["GET /api/a"])
|
|
134
134
|
|
|
135
135
|
|
|
136
|
+
class SecretPrecisionTests(unittest.TestCase):
|
|
137
|
+
"""bug-072: low-precision generic/entropy secret rules -> MEDIUM (+verify note); specific
|
|
138
|
+
rules (AKIA, private-key, …) keep HIGH. Nothing is hidden."""
|
|
139
|
+
|
|
140
|
+
def test_generic_rule_detection(self):
|
|
141
|
+
self.assertTrue(scanners._generic_secret("generic-api-key"))
|
|
142
|
+
self.assertTrue(scanners._generic_secret("high-entropy-string"))
|
|
143
|
+
self.assertFalse(scanners._generic_secret("aws-access-token"))
|
|
144
|
+
self.assertFalse(scanners._generic_secret("private-key"))
|
|
145
|
+
|
|
146
|
+
def test_gitleaks_generic_is_medium_specific_is_high(self):
|
|
147
|
+
rows = [
|
|
148
|
+
{"File": "src/lib/chains.ts", "RuleID": "generic-api-key", "Secret": "x" * 40, "Match": "x" * 40, "StartLine": 1},
|
|
149
|
+
{"File": "src/k.pem", "RuleID": "private-key", "Secret": "-----BEGIN", "Match": "-----BEGIN", "StartLine": 1},
|
|
150
|
+
{"File": "src/a.ts", "RuleID": "aws-access-token", "Secret": "AKIA" + "A" * 16, "Match": "AKIA" + "A" * 16, "StartLine": 1},
|
|
151
|
+
]
|
|
152
|
+
by = {r["key"]: r for r in scanners._norm_gitleaks(rows)}
|
|
153
|
+
self.assertEqual(by["generic-api-key"]["severity"], "MEDIUM")
|
|
154
|
+
self.assertIn("generic/entropy", by["generic-api-key"]["title"])
|
|
155
|
+
self.assertEqual(by["private-key"]["severity"], "HIGH") # specific rule untouched
|
|
156
|
+
self.assertEqual(by["aws-access-token"]["severity"], "HIGH") # AKIA via _aws_secret_tier
|
|
157
|
+
|
|
158
|
+
def test_trivy_generic_secret_is_medium(self):
|
|
159
|
+
data = {"Results": [{"Target": "src/x.ts", "Secrets": [
|
|
160
|
+
{"RuleID": "generic-api-key", "Title": "Generic API Key", "Match": "y" * 40, "StartLine": 2}]}]}
|
|
161
|
+
secs = [f for f in scanners._norm_trivy(data) if f["category"] == "secret"]
|
|
162
|
+
self.assertEqual(secs[0]["severity"], "MEDIUM")
|
|
163
|
+
|
|
164
|
+
def test_medium_secret_gets_medium_confidence_in_ledger(self):
|
|
165
|
+
unified = {"top": [{"severity": "MEDIUM", "category": "secret",
|
|
166
|
+
"title": "secret: Generic API Key — generic/entropy match", "file": "src/x.ts", "tools": ["gitleaks"]}]}
|
|
167
|
+
led = findings.build_ledger({}, unified, None, [])
|
|
168
|
+
hit = [f for f in led["findings"] if f["category"] == "static-secret"][0]
|
|
169
|
+
self.assertEqual(hit["confidence"], "MEDIUM")
|
|
170
|
+
|
|
171
|
+
|
|
136
172
|
if __name__ == "__main__":
|
|
137
173
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/client_exposure.py
RENAMED
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/graphql.py
RENAMED
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/integrations.py
RENAMED
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/schemas.py
RENAMED
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/extractors/surface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/_lib.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator/templates/probes/s3-assess.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{websec_validator-0.2.5 → websec_validator-0.2.6}/src/websec_validator.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|