pcap-doctor 0.2.0__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.
- pcap_doctor-0.2.0/.github/ISSUE_TEMPLATE/bug.md +43 -0
- pcap_doctor-0.2.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- pcap_doctor-0.2.0/.github/ISSUE_TEMPLATE/core-change.md +34 -0
- pcap_doctor-0.2.0/.github/ISSUE_TEMPLATE/detector.md +53 -0
- pcap_doctor-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +31 -0
- pcap_doctor-0.2.0/.github/workflows/action-selftest.yml +30 -0
- pcap_doctor-0.2.0/.github/workflows/ci.yml +58 -0
- pcap_doctor-0.2.0/.github/workflows/release.yml +84 -0
- pcap_doctor-0.2.0/.gitignore +25 -0
- pcap_doctor-0.2.0/AGENTS.md +118 -0
- pcap_doctor-0.2.0/CHANGELOG.md +168 -0
- pcap_doctor-0.2.0/CONTRIBUTING.md +136 -0
- pcap_doctor-0.2.0/LICENSE +32 -0
- pcap_doctor-0.2.0/Makefile +66 -0
- pcap_doctor-0.2.0/NOTICE +24 -0
- pcap_doctor-0.2.0/PKG-INFO +710 -0
- pcap_doctor-0.2.0/README.md +691 -0
- pcap_doctor-0.2.0/action.yml +78 -0
- pcap_doctor-0.2.0/captures/dns-mdns.pcap +0 -0
- pcap_doctor-0.2.0/captures/dns_port.pcap +0 -0
- pcap_doctor-0.2.0/captures/dtls12-aes128ccm8.pcap +0 -0
- pcap_doctor-0.2.0/captures/ntp.pcap +0 -0
- pcap_doctor-0.2.0/captures/quic-with-secrets.pcapng +0 -0
- pcap_doctor-0.2.0/captures/retrans-tls.pcap +0 -0
- pcap_doctor-0.2.0/captures/sip-rtp.pcapng +0 -0
- pcap_doctor-0.2.0/captures/sip.pcapng +0 -0
- pcap_doctor-0.2.0/captures/snakeoil-dtls.pcap +0 -0
- pcap_doctor-0.2.0/captures/tftp.pcap +0 -0
- pcap_doctor-0.2.0/captures/tls-renegotiation.pcap +0 -0
- pcap_doctor-0.2.0/captures/tls12-aes128ccm.pcap +0 -0
- pcap_doctor-0.2.0/captures/tls12-aes256gcm.pcap +0 -0
- pcap_doctor-0.2.0/captures/tls12-chacha20poly1305.pcap +0 -0
- pcap_doctor-0.2.0/captures/tls13-rfc8446.pcap +0 -0
- pcap_doctor-0.2.0/docs/architecture.md +96 -0
- pcap_doctor-0.2.0/docs/assets/logo-dark.svg +9 -0
- pcap_doctor-0.2.0/docs/assets/logo-light.svg +9 -0
- pcap_doctor-0.2.0/docs/cipher-policy.md +115 -0
- pcap_doctor-0.2.0/docs/detector-authoring.md +154 -0
- pcap_doctor-0.2.0/docs/issue-board.md +138 -0
- pcap_doctor-0.2.0/docs/severity-model.md +56 -0
- pcap_doctor-0.2.0/docs/subagent-playbook.md +154 -0
- pcap_doctor-0.2.0/docs/tshark-fields.md +70 -0
- pcap_doctor-0.2.0/npm/bin/pcap-doctor.js +29 -0
- pcap_doctor-0.2.0/npm/package.json +22 -0
- pcap_doctor-0.2.0/npm/test/launcher.test.js +50 -0
- pcap_doctor-0.2.0/pyproject.toml +57 -0
- pcap_doctor-0.2.0/scripts/fetch_captures.sh +47 -0
- pcap_doctor-0.2.0/scripts/gen_cipher_suites.py +294 -0
- pcap_doctor-0.2.0/scripts/make_fixtures.py +663 -0
- pcap_doctor-0.2.0/scripts/pcap-doctor-action.sh +97 -0
- pcap_doctor-0.2.0/src/pcapforensics/__init__.py +6 -0
- pcap_doctor-0.2.0/src/pcapforensics/baseline.py +27 -0
- pcap_doctor-0.2.0/src/pcapforensics/certificates.py +188 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/__init__.py +47 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/__main__.py +5 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/_console.py +43 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/_summary.py +44 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/analyze.py +123 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/ci.py +47 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/inspect.py +133 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/install.py +82 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/rules.py +51 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/watch.py +122 -0
- pcap_doctor-0.2.0/src/pcapforensics/cli/why.py +73 -0
- pcap_doctor-0.2.0/src/pcapforensics/config.py +187 -0
- pcap_doctor-0.2.0/src/pcapforensics/data/cipher_names.tsv +424 -0
- pcap_doctor-0.2.0/src/pcapforensics/data/cipher_suites.json +6257 -0
- pcap_doctor-0.2.0/src/pcapforensics/data_ciphers.py +194 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/__init__.py +1 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/_template.py +52 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/base.py +76 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/dns_quic_ssh.py +362 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/sip_rtp.py +232 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/tls_cipher.py +573 -0
- pcap_doctor-0.2.0/src/pcapforensics/detectors/transport_exposure.py +428 -0
- pcap_doctor-0.2.0/src/pcapforensics/handoff.py +162 -0
- pcap_doctor-0.2.0/src/pcapforensics/index.py +1117 -0
- pcap_doctor-0.2.0/src/pcapforensics/models.py +465 -0
- pcap_doctor-0.2.0/src/pcapforensics/output.py +96 -0
- pcap_doctor-0.2.0/src/pcapforensics/pipeline.py +101 -0
- pcap_doctor-0.2.0/src/pcapforensics/policy.py +49 -0
- pcap_doctor-0.2.0/src/pcapforensics/prompts.py +61 -0
- pcap_doctor-0.2.0/src/pcapforensics/registry.py +52 -0
- pcap_doctor-0.2.0/src/pcapforensics/render/__init__.py +1 -0
- pcap_doctor-0.2.0/src/pcapforensics/render/markdown.py +532 -0
- pcap_doctor-0.2.0/src/pcapforensics/render/mermaid.py +128 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/BEACONING_SHAPE.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/CLEARTEXT_CREDENTIAL.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/CLEARTEXT_SERVICE.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/DNS_CLEARTEXT.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/DNS_EXTERNAL_RESOLVER.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/DNS_TUNNEL_SHAPE.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/HTTP_BASIC_AUTH.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/HTTP_CLEARTEXT.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/HTTP_CLEARTEXT_AUTH.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/HTTP_COOKIE_NO_SECURE.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/QUIC_PAYLOAD_OPAQUE.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/RTP_MEDIA_UNPROTECTED.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/RTP_VOLUME_ANOMALY.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SDP_NO_CRYPTO_ATTR.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SERVICE_ON_ODD_PORT.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SIP_CLEARTEXT_SIGNALLING.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SSH_TERRAPIN_EXPOSED.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SSH_WEAK_CIPHER.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SSH_WEAK_HOSTKEY.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SSH_WEAK_KEX.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SSH_WEAK_MAC.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/SYN_SCAN_SHAPE.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CERT_EXPIRED.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CERT_EXPIRED_NOW.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CERT_EXPIRING.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CERT_WEAK_KEY.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CERT_WEAK_SIGALG.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CIPHER_UNKNOWN.md +15 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_CIPHER_WEAK.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_FATAL_ALERT.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_HANDSHAKE_TRUNCATED.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_JA3_FLEET.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_LEGACY_RECORD_VERSION.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_NO_FORWARD_SECRECY.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_OFFERS_WEAK_CIPHERS.md +14 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_SELF_SIGNED_CHAIN.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_VERSION_DEPRECATED.md +16 -0
- pcap_doctor-0.2.0/src/pcapforensics/rule_docs/TLS_WARNING_ALERT.md +13 -0
- pcap_doctor-0.2.0/src/pcapforensics/rules.py +75 -0
- pcap_doctor-0.2.0/src/pcapforensics/scoring.py +35 -0
- pcap_doctor-0.2.0/src/pcapforensics/templates/agent-guide.md +20 -0
- pcap_doctor-0.2.0/src/pcapforensics/templates/workflow.yml +25 -0
- pcap_doctor-0.2.0/src/pcapforensics/tshark.py +805 -0
- pcap_doctor-0.2.0/tests/conftest.py +82 -0
- pcap_doctor-0.2.0/tests/fixtures/beaconing.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/dns_external.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/dns_tunnel.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/ftp_ldap_creds.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/http_bare_token.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/http_basic.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/http_cleartext.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/mixed_ciphers.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/no_pfs_tls12.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/sip_rtp.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/snmp_creds.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/ssh_weak.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/strong_tls13.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/syn_scan.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/telnet_login.pcap +0 -0
- pcap_doctor-0.2.0/tests/fixtures/weak_tls.pcap +0 -0
- pcap_doctor-0.2.0/tests/test_baseline.py +80 -0
- pcap_doctor-0.2.0/tests/test_ci_install.py +91 -0
- pcap_doctor-0.2.0/tests/test_cipher_registry.py +147 -0
- pcap_doctor-0.2.0/tests/test_citations.py +41 -0
- pcap_doctor-0.2.0/tests/test_cli_smoke.py +27 -0
- pcap_doctor-0.2.0/tests/test_config.py +108 -0
- pcap_doctor-0.2.0/tests/test_corpus.py +288 -0
- pcap_doctor-0.2.0/tests/test_detectors.py +508 -0
- pcap_doctor-0.2.0/tests/test_docs.py +77 -0
- pcap_doctor-0.2.0/tests/test_handoff.py +133 -0
- pcap_doctor-0.2.0/tests/test_http_cleartext.py +63 -0
- pcap_doctor-0.2.0/tests/test_install.py +51 -0
- pcap_doctor-0.2.0/tests/test_json_out.py +53 -0
- pcap_doctor-0.2.0/tests/test_policy.py +68 -0
- pcap_doctor-0.2.0/tests/test_profile.py +74 -0
- pcap_doctor-0.2.0/tests/test_prompts.py +64 -0
- pcap_doctor-0.2.0/tests/test_rebrand.py +27 -0
- pcap_doctor-0.2.0/tests/test_release_meta.py +49 -0
- pcap_doctor-0.2.0/tests/test_review_fixes.py +123 -0
- pcap_doctor-0.2.0/tests/test_rules_catalog.py +52 -0
- pcap_doctor-0.2.0/tests/test_rules_cli.py +40 -0
- pcap_doctor-0.2.0/tests/test_sarif.py +78 -0
- pcap_doctor-0.2.0/tests/test_scoring.py +38 -0
- pcap_doctor-0.2.0/tests/test_summary.py +67 -0
- pcap_doctor-0.2.0/tests/test_tshark_layer.py +236 -0
- pcap_doctor-0.2.0/tests/test_watch.py +70 -0
- pcap_doctor-0.2.0/tests/test_why.py +80 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug
|
|
3
|
+
about: Something the analyzer gets wrong, or cannot run at all
|
|
4
|
+
title: "bug: "
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**What happened**
|
|
10
|
+
|
|
11
|
+
**What it should have done**
|
|
12
|
+
|
|
13
|
+
**Reproduce**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pcap-doctor analyze <capture> --out /tmp/x --no-cache
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Wrong output, verbatim**
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
<paste the finding, the note, or the traceback -- exactly as printed>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Ground truth**
|
|
26
|
+
|
|
27
|
+
How you know it is wrong. `tshark -r <capture> -Y '<filter>' -T fields -e <field>` output is ideal.
|
|
28
|
+
|
|
29
|
+
- [ ] `tshark` version: `pcap-doctor doctor`
|
|
30
|
+
- [ ] capture sha256: `pcap-doctor analyze <capture> | head -5`
|
|
31
|
+
- [ ] cache cleared or `--no-cache` used
|
|
32
|
+
|
|
33
|
+
**Scope**
|
|
34
|
+
|
|
35
|
+
- [ ] parser/index (core-owned)
|
|
36
|
+
- [ ] a detector
|
|
37
|
+
- [ ] rendering
|
|
38
|
+
- [ ] tshark field drift
|
|
39
|
+
|
|
40
|
+
**Suggested location**
|
|
41
|
+
|
|
42
|
+
File and function, if you know it. If this is a schema gap, propose the field rather than working
|
|
43
|
+
around it in a detector: `AGENTS.md` §1.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Severity and confidence policy
|
|
4
|
+
url: https://github.com/Vaibhav91one/pcap-forensics/blob/main/docs/severity-model.md
|
|
5
|
+
about: How to choose severity and confidence for a finding.
|
|
6
|
+
- name: Writing a detector
|
|
7
|
+
url: https://github.com/Vaibhav91one/pcap-forensics/blob/main/docs/detector-authoring.md
|
|
8
|
+
about: The detector contract, the index API, and the fixture rules.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Core change
|
|
3
|
+
about: Change models.py, index.py, tshark.py, data_ciphers.py or a renderer
|
|
4
|
+
title: "core: "
|
|
5
|
+
labels: core
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Core changes are a **merge event**, not a parallel one (`docs/subagent-playbook.md`).
|
|
10
|
+
|
|
11
|
+
**What changes**
|
|
12
|
+
|
|
13
|
+
Paths: `models.py` / `index.py` / `tshark.py` / `data_ciphers.py` / `certificates.py` / `render/*`
|
|
14
|
+
|
|
15
|
+
**Does `report.json`'s schema change?**
|
|
16
|
+
|
|
17
|
+
- [ ] no — additive only, `schema_version` unchanged
|
|
18
|
+
- [ ] yes, additive — bump minor
|
|
19
|
+
- [ ] yes, breaking — bump major, and list the migration in the issue
|
|
20
|
+
|
|
21
|
+
**If a new field is proposed**
|
|
22
|
+
|
|
23
|
+
| Field | Type | Example from a real capture | Read by |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| | | | |
|
|
26
|
+
|
|
27
|
+
**Which detectors are blocked without it**
|
|
28
|
+
|
|
29
|
+
**Migration / rollout**
|
|
30
|
+
|
|
31
|
+
- [ ] tests updated or added
|
|
32
|
+
- [ ] `docs/` updated (tshark-fields, severity-model, cipher-policy as applicable)
|
|
33
|
+
- [ ] `make verify` green
|
|
34
|
+
- [ ] agents holding open branches told to rebase
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Detector
|
|
3
|
+
about: Add a detector (one check, one file, one claim)
|
|
4
|
+
title: "detector: "
|
|
5
|
+
labels: detector
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Which detector file**
|
|
10
|
+
|
|
11
|
+
`src/pcapforensics/detectors/<name>.py` — new or existing.
|
|
12
|
+
|
|
13
|
+
**What it detects**
|
|
14
|
+
|
|
15
|
+
One sentence, in the words an analyst would use. Not "analyzes X" — "flags when Y".
|
|
16
|
+
|
|
17
|
+
**Why it matters**
|
|
18
|
+
|
|
19
|
+
What is the consequence if the condition is true and the tool is right?
|
|
20
|
+
|
|
21
|
+
**Input that reproduces it**
|
|
22
|
+
|
|
23
|
+
- capture: `<path or how to build it>`
|
|
24
|
+
- observable: `<the exact frame, field, or shape>`
|
|
25
|
+
- expected output:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
<one line: the finding code, severity, confidence>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Fields it needs from CaptureIndex**
|
|
32
|
+
|
|
33
|
+
| Field | Type | Example value | Already present? |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| `index.…` | | | yes / no (file an issue if no) |
|
|
36
|
+
|
|
37
|
+
**Fixture**
|
|
38
|
+
|
|
39
|
+
- [ ] added to `scripts/make_fixtures.py` and regenerated (`make fixtures`)
|
|
40
|
+
- [ ] deterministic (no randomness, no clock, no network)
|
|
41
|
+
- [ ] negative case too: a fixture where this must stay silent
|
|
42
|
+
|
|
43
|
+
**Checklist (AGENTS.md §3)**
|
|
44
|
+
|
|
45
|
+
- [ ] pure `detect(index) -> list[Finding]`, no I/O
|
|
46
|
+
- [ ] stable `code` and `scope`; ids stable across runs
|
|
47
|
+
- [ ] every finding has frame-numbered evidence with a non-empty value
|
|
48
|
+
- [ ] every finding has a `remediation`, plus references where a standard applies
|
|
49
|
+
- [ ] unknown input yields a finding or a note, never a silent pass
|
|
50
|
+
- [ ] no secret material can reach an artifact
|
|
51
|
+
- [ ] tests assert on codes, not prose
|
|
52
|
+
- [ ] README detector table has its row
|
|
53
|
+
- [ ] `make verify` green, `pcap-doctor doctor` passes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Closes #
|
|
2
|
+
|
|
3
|
+
## What I saw
|
|
4
|
+
|
|
5
|
+
<!-- The bug or gap, with the capture/fixture and frame that shows it. -->
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
|
|
9
|
+
<!-- Files touched and why. Stay inside the allowlist on the issue (AGENTS.md §1-§2). -->
|
|
10
|
+
|
|
11
|
+
## What I verified
|
|
12
|
+
|
|
13
|
+
<!-- Paste the evidence, not a claim. -->
|
|
14
|
+
|
|
15
|
+
- [ ] The new test **fails on the old code** (paste the failing line) and passes after
|
|
16
|
+
- [ ] `make verify` is green (ruff, mypy strict, pytest)
|
|
17
|
+
- [ ] `pcap-doctor doctor` passes
|
|
18
|
+
- [ ] New or changed fixtures are generated by `scripts/make_fixtures.py` and regenerate byte-for-byte
|
|
19
|
+
|
|
20
|
+
## Deliberately not done
|
|
21
|
+
|
|
22
|
+
<!-- Follow-ups, known limits, anything out of scope. File issues for them. -->
|
|
23
|
+
|
|
24
|
+
## Checklist (AGENTS.md §3)
|
|
25
|
+
|
|
26
|
+
- [ ] Every finding cites a real frame and has a stable `code` and `scope`
|
|
27
|
+
- [ ] Every finding has a remediation and, when a standard applies, a reference a reader can look up
|
|
28
|
+
- [ ] No secret can reach a report (redacted, with a test that greps the artifacts)
|
|
29
|
+
- [ ] Heuristics report `confidence: low`; severity is about impact, not urgency
|
|
30
|
+
- [ ] Tests assert on finding codes, not prose
|
|
31
|
+
- [ ] README detector table and CHANGELOG updated if behaviour changed
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Runs this repository's own action (action.yml) on pull requests that change it, against the test fixtures.
|
|
2
|
+
name: action-selftest
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
paths:
|
|
7
|
+
- action.yml
|
|
8
|
+
- scripts/pcap-doctor-action.sh
|
|
9
|
+
- src/**
|
|
10
|
+
- .github/workflows/action-selftest.yml
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pull-requests: write
|
|
15
|
+
security-events: write
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
selftest:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- id: doctor
|
|
23
|
+
uses: ./
|
|
24
|
+
with:
|
|
25
|
+
captures: "tests/fixtures/*.pcap"
|
|
26
|
+
# This job proves the action runs end to end (install, analysis, comment, SARIF). The gate itself is
|
|
27
|
+
# tested in tests/test_ci_install.py; gating here would fail every PR that adds a fixture.
|
|
28
|
+
fail-on: none
|
|
29
|
+
- name: The summary lists every fixture
|
|
30
|
+
run: test "$(grep -c '^| `tests/fixtures/' "${{ steps.doctor.outputs.summary }}")" -eq "$(ls tests/fixtures/*.pcap | wc -l)"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
verify:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- name: Install tshark
|
|
17
|
+
run: |
|
|
18
|
+
sudo apt-get update
|
|
19
|
+
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tshark
|
|
20
|
+
tshark --version | head -1
|
|
21
|
+
- uses: astral-sh/setup-uv@v5
|
|
22
|
+
- name: Install project
|
|
23
|
+
run: |
|
|
24
|
+
uv venv
|
|
25
|
+
uv pip install -e '.[dev]'
|
|
26
|
+
- name: Environment check
|
|
27
|
+
run: uv run pcap-doctor doctor
|
|
28
|
+
- name: Fetch regression corpus
|
|
29
|
+
run: ./scripts/fetch_captures.sh
|
|
30
|
+
- name: Fixtures are reproducible
|
|
31
|
+
run: |
|
|
32
|
+
make fixtures
|
|
33
|
+
static=$(.venv/bin/python -c "import sys; sys.path.insert(0, 'scripts'); import make_fixtures as m; print(' '.join('tests/fixtures/' + n for n in m.STATIC_FIXTURES))")
|
|
34
|
+
git diff --exit-code -- $static || (echo "byte-built fixtures drifted; commit the regenerated files" && exit 1)
|
|
35
|
+
- name: Lint
|
|
36
|
+
run: .venv/bin/ruff check .
|
|
37
|
+
- name: Typecheck
|
|
38
|
+
run: .venv/bin/mypy
|
|
39
|
+
- name: Tests
|
|
40
|
+
run: .venv/bin/python -m pytest -q
|
|
41
|
+
- name: Reports render end to end
|
|
42
|
+
run: |
|
|
43
|
+
for cap in captures/*.pcap captures/*.pcapng; do
|
|
44
|
+
.venv/bin/pcap-doctor analyze "$cap" --out /tmp/pf-ci --quiet || true
|
|
45
|
+
done
|
|
46
|
+
test -s /tmp/pf-ci/report.json
|
|
47
|
+
test -s /tmp/pf-ci/04-diagrams.md
|
|
48
|
+
|
|
49
|
+
npm-launcher:
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
- uses: actions/setup-node@v4
|
|
54
|
+
with:
|
|
55
|
+
node-version: "20"
|
|
56
|
+
- name: Launcher tests
|
|
57
|
+
working-directory: npm
|
|
58
|
+
run: npm test
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a version tag is pushed (issue #50), then the npm launcher (issue #51).
|
|
4
|
+
# Both use trusted publishing (OIDC): no token or secret exists for either registry (#93).
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v5
|
|
18
|
+
- name: Tag matches the package version
|
|
19
|
+
run: |
|
|
20
|
+
version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
21
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
22
|
+
echo "tag $GITHUB_REF_NAME does not match pyproject version v$version"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
npm_version=$(node -p "require('./npm/package.json').version")
|
|
26
|
+
if [ "$npm_version" != "$version" ]; then
|
|
27
|
+
echo "npm/package.json version $npm_version does not match pyproject version $version"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
- run: uv build
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
|
|
36
|
+
pypi:
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment:
|
|
40
|
+
name: pypi
|
|
41
|
+
url: https://pypi.org/p/pcap-doctor
|
|
42
|
+
permissions:
|
|
43
|
+
id-token: write
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/download-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
|
+
|
|
51
|
+
npm:
|
|
52
|
+
needs: pypi
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
id-token: write # npm trusted publishing: GitHub proves the workflow's identity; provenance is automatic
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: actions/setup-node@v4
|
|
60
|
+
with:
|
|
61
|
+
node-version: "22"
|
|
62
|
+
registry-url: https://registry.npmjs.org
|
|
63
|
+
- name: npm with trusted publishing (needs 11.5.1+)
|
|
64
|
+
run: npm install -g npm@latest
|
|
65
|
+
- name: Launcher tests
|
|
66
|
+
working-directory: npm
|
|
67
|
+
run: npm test
|
|
68
|
+
- name: Publish unless the package is new or this version is already on npm
|
|
69
|
+
working-directory: npm
|
|
70
|
+
run: |
|
|
71
|
+
version=$(node -p "require('./package.json').version")
|
|
72
|
+
if ! versions=$(npm view pcap-doctor versions --json 2>&1); then
|
|
73
|
+
if grep -q E404 <<< "$versions"; then
|
|
74
|
+
echo "::notice::pcap-doctor is not on npm yet. A trusted publisher can only be added to an existing package: publish it once by hand (cd npm && npm login && npm publish --access public), then add this workflow as its trusted publisher."
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
echo "$versions"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
if node -e 'process.exit([].concat(JSON.parse(process.argv[1])).includes(process.argv[2]) ? 0 : 1)' "$versions" "$version"; then
|
|
81
|
+
echo "::notice::pcap-doctor@$version is already on npm; nothing to publish."
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
npm publish --access public
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# environments
|
|
2
|
+
.venv/
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*.egg-info/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# tooling caches
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
|
|
16
|
+
# analysis output
|
|
17
|
+
out/
|
|
18
|
+
*.pf-report/
|
|
19
|
+
|
|
20
|
+
# tshark pass cache
|
|
21
|
+
.cache/
|
|
22
|
+
|
|
23
|
+
# NOTE: captures/ is intentionally NOT ignored in this repo -- the regression
|
|
24
|
+
# suite needs it and the files are small. Set PF_SKIP_CAPTURES=1 in CI to
|
|
25
|
+
# download them at run time instead of storing them.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# AGENTS.md — the contract for automated subagents
|
|
2
|
+
|
|
3
|
+
This file is binding for any agent (human or not) working in this repository. It exists so that
|
|
4
|
+
four agents can work at once, in isolation, without stepping on each other.
|
|
5
|
+
|
|
6
|
+
Read this before you touch anything. Then read `docs/detector-authoring.md` if you are adding a
|
|
7
|
+
detector, or `docs/subagent-playbook.md` if you are running a subagent loop.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Ownership map
|
|
12
|
+
|
|
13
|
+
Exactly one owner per file class. If a task needs a file you do not own, you file an issue; you do
|
|
14
|
+
not edit it.
|
|
15
|
+
|
|
16
|
+
| Path | Owner | Notes |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `src/pcapforensics/models.py` | **core** | the schema. frozen after Phase 0 |
|
|
19
|
+
| `src/pcapforensics/index.py` | **core** | builds the only view detectors get |
|
|
20
|
+
| `src/pcapforensics/tshark.py` | **core** | the tshark boundary |
|
|
21
|
+
| `src/pcapforensics/data_ciphers.py` | **core** | crypto reasoning helpers |
|
|
22
|
+
| `src/pcapforensics/certificates.py` | **core** | openssl enrichment |
|
|
23
|
+
| `src/pcapforensics/data/*` | **core** | generated by `make regenerate` |
|
|
24
|
+
| `src/pcapforensics/detectors/<yourname>.py` | **you** | one detector, one file |
|
|
25
|
+
| `src/pcapforensics/render/*` | **core** | detectors must not add renderers |
|
|
26
|
+
| `src/pcapforensics/cli/__init__.py`, `cli/_console.py` | **core** | the app and the command loader |
|
|
27
|
+
| `src/pcapforensics/cli/<command>.py` | **you** (one command, one file) | exports `register(app)`; never edit another command's file |
|
|
28
|
+
| `tests/test_cipher_registry.py`, `test_tshark_layer.py` | **core** | |
|
|
29
|
+
| `tests/fixtures/<yours>.pcap` + its test | **you** | |
|
|
30
|
+
| `README.md` detector table row for your detector | **you** (one line) | do not touch other sections |
|
|
31
|
+
| `pyproject.toml` | **core** | new runtime dependencies require an issue |
|
|
32
|
+
|
|
33
|
+
## 2. Isolation rules
|
|
34
|
+
|
|
35
|
+
1. **Stay inside your allowlist.** Your allowlist is written on your issue. Edits outside it are
|
|
36
|
+
rejected in review even when they look like an improvement.
|
|
37
|
+
2. **One issue, one branch, one PR.** Branch name `agent/<issue-number>-<slug>`.
|
|
38
|
+
3. **Never import a sibling detector.** `detectors/a.py` must not import `detectors/b.py`. If you
|
|
39
|
+
need shared logic, it goes into `index.py` (core-owned) via an issue.
|
|
40
|
+
4. **Never edit `models.py` to make your life easier.** Add the field in an issue with a proposed
|
|
41
|
+
shape. Schema changes are a merge event, not a parallel event.
|
|
42
|
+
5. **No new runtime dependency** without an issue. Dev dependencies (pytest, scapy) are fine.
|
|
43
|
+
6. **Do not rebase or force-push** other agents' branches. If you need a core change, land the
|
|
44
|
+
core issue first and rebase onto it.
|
|
45
|
+
|
|
46
|
+
## 3. What "done" means
|
|
47
|
+
|
|
48
|
+
A task is done when **all** of these are true. Review checks them mechanically.
|
|
49
|
+
|
|
50
|
+
- [ ] The detector lives in its own file, is named `dN.<slug>`, and has `enabled = True` only if
|
|
51
|
+
it produces findings on at least one fixture.
|
|
52
|
+
- [ ] `detect()` is a pure function of the `CaptureIndex`: no file I/O, no network, no globals.
|
|
53
|
+
- [ ] Every finding has a **stable** `code` (SCREAMING_SNAKE) and a `scope` that is unique per
|
|
54
|
+
flow/host so `Finding.id` is stable across runs.
|
|
55
|
+
- [ ] Every finding carries at least one `Evidence` with a real frame number and a non-empty value.
|
|
56
|
+
- [ ] Every finding carries `remediation` and at least one `references` entry when a standard
|
|
57
|
+
applies. A finding a reader cannot act on is not finished.
|
|
58
|
+
- [ ] Unknown or unparseable input produces a **finding or a note**, never a silent pass.
|
|
59
|
+
- [ ] No secret material can reach a report. If you touch credentials, use
|
|
60
|
+
`index.redact_auth` or add a redaction of the same shape, and add a test.
|
|
61
|
+
- [ ] A synthetic fixture exists under `tests/fixtures/`, is generated by
|
|
62
|
+
`scripts/make_fixtures.py`, and is deterministic.
|
|
63
|
+
- [ ] Tests assert on **finding codes**, not on prose.
|
|
64
|
+
- [ ] Every new finding `code` has a `RULES` entry in `rules.py` (category and title) and a
|
|
65
|
+
`rule_docs/<CODE>.md` with the four sections *What it means*, *Why it matters*, *How to fix*
|
|
66
|
+
and *How to verify*. `tests/test_rules_catalog.py` and `tests/test_prompts.py` fail otherwise.
|
|
67
|
+
- [ ] The detector row in the README table is updated.
|
|
68
|
+
- [ ] `make verify` is green: `ruff check .`, `mypy` (strict), `pytest`.
|
|
69
|
+
- [ ] `pcap-doctor doctor` still passes.
|
|
70
|
+
|
|
71
|
+
## 4. Honesty rules
|
|
72
|
+
|
|
73
|
+
These are the project's rules about itself, and a reviewer will fail a PR that breaks them.
|
|
74
|
+
|
|
75
|
+
- Do not claim a fact the capture does not contain. If forward secrecy is undecidable, the answer
|
|
76
|
+
is `None` with a reason, not `False`.
|
|
77
|
+
- Do not round a heuristic into a certainty. If it is a heuristic, set `confidence="low"`.
|
|
78
|
+
- Do not use a severity to express urgency rather than impact.
|
|
79
|
+
- Do not suppress a finding because it is noisy. If it is noisy, that is a policy conversation,
|
|
80
|
+
and the policy lives in `docs/severity-model.md`.
|
|
81
|
+
- If you find a bug in core while working, **do not fix it in your PR**. File it, link it, and keep
|
|
82
|
+
your detector working against the current contract.
|
|
83
|
+
|
|
84
|
+
## 5. Severity and confidence
|
|
85
|
+
|
|
86
|
+
Severity answers "how bad is this if true"; confidence answers "how sure am I from this capture".
|
|
87
|
+
They are independent and both are required.
|
|
88
|
+
|
|
89
|
+
| Severity | Use when |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `critical` | credential or key material is exposed, or a prohibited cipher is negotiated |
|
|
92
|
+
| `high` | confidentiality of captured traffic is at risk without user action (no PFS, deprecated version, cleartext media) |
|
|
93
|
+
| `medium` | downgrade surface, policy violations, likely-relevant heuristics |
|
|
94
|
+
| `low` | legacy fields, weak hints, context a reviewer should see |
|
|
95
|
+
| `info` | inventory and clustering that is not a defect on its own |
|
|
96
|
+
|
|
97
|
+
`confidence` is `high` only when the evidence is directly visible in the capture.
|
|
98
|
+
|
|
99
|
+
## 6. Communication
|
|
100
|
+
|
|
101
|
+
- Issue title: `detector: <what it detects>` or `core: <what changes>`.
|
|
102
|
+
- Issue body must name the affected paths, the input that reproduces it, and the expected output.
|
|
103
|
+
- PR description: what you saw, what you changed, what you verified, and what you deliberately did
|
|
104
|
+
not do.
|
|
105
|
+
- If you could not finish, say so in the PR. A partial detector behind `enabled = False` is a
|
|
106
|
+
useful contribution; a half-working enabled detector is not.
|
|
107
|
+
|
|
108
|
+
## 7. Where the truth lives
|
|
109
|
+
|
|
110
|
+
| Question | File |
|
|
111
|
+
|---|---|
|
|
112
|
+
| What can I read from the index? | `src/pcapforensics/models.py` |
|
|
113
|
+
| How is a cipher judged weak? | `docs/cipher-policy.md` |
|
|
114
|
+
| How is severity chosen? | `docs/severity-model.md` |
|
|
115
|
+
| Which tshark fields do we rely on? | `docs/tshark-fields.md` |
|
|
116
|
+
| How do I write a detector? | `docs/detector-authoring.md` |
|
|
117
|
+
| How do I run a fleet of subagents? | `docs/subagent-playbook.md` |
|
|
118
|
+
| What is frozen and why? | `AGENTS.md` §1 |
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] — 2026-09-30
|
|
10
|
+
|
|
11
|
+
**pcap-forensics is now pcap-doctor**: one command gives a health score, findings grouped by category,
|
|
12
|
+
explanations, a CI gate on new findings, and a handoff to AI coding agents. `pf` stays as an alias.
|
|
13
|
+
`report.json` keeps `schema_version` `1.4.0`: everything new is computed around the report.
|
|
14
|
+
|
|
15
|
+
### Added — pcap-doctor
|
|
16
|
+
|
|
17
|
+
- Distribution and command `pcap-doctor`, `--version`; `npx pcap-doctor` launcher (runs the same
|
|
18
|
+
version through `uvx` or `pipx`); PyPI and npm releases by trusted publishing,
|
|
19
|
+
with no stored token (#46, #50, #51, #93).
|
|
20
|
+
- The CLI is a package of self-registering command modules (#45).
|
|
21
|
+
- A rule catalog with eight categories, `rules list` / `rules explain`, and a doc per finding code
|
|
22
|
+
(#47, #49, #61).
|
|
23
|
+
- A local 0-100 health score and a summary grouped by category; `--score`, `--verbose` (#48, #53).
|
|
24
|
+
- `--category`, strict option validation (unknown values exit 2 before tshark runs) (#52).
|
|
25
|
+
- `--json` / `--json-out` envelope, `--sarif` (SARIF 2.1.0), `--baseline` to show and gate only new
|
|
26
|
+
findings (#54, #55, #56).
|
|
27
|
+
- `pcap-doctor.toml` / `[tool.pcap-doctor]`: `disable`, `[severity]`, `[[allow]]` with a reason,
|
|
28
|
+
`categories`, `fail_on`, and the `ota` profile (`--config`, `--profile`); every suppression is
|
|
29
|
+
noted in the report (#57, #58).
|
|
30
|
+
- `why <frame|id>` with `--prompt`; an AI handoff menu after interactive scans (Claude Code, Codex,
|
|
31
|
+
Cursor; `--safe`, `--no-handoff`), agent guidance inside agent shells, and a fenced
|
|
32
|
+
untrusted-data prompt builder (#49, #59, #60).
|
|
33
|
+
- `install` writes agent guides (Claude Code skill, Cursor rule, AGENTS.md block) (#62).
|
|
34
|
+
- `ci install` and a composite GitHub Action that compares each capture with its base-branch copy,
|
|
35
|
+
comments once on the PR and uploads SARIF (#63).
|
|
36
|
+
- `watch -i IFACE`: ring-buffer live capture that prints each finding the first time it appears (#64).
|
|
37
|
+
- `HTTP_CLEARTEXT`: plain HTTP is reported per flow, high when a request path looks like a firmware or
|
|
38
|
+
package download (so `--profile ota` fails it) and medium otherwise; query strings are never written
|
|
39
|
+
to a report (#91). A new fixture, `http_cleartext.pcap`, covers it.
|
|
40
|
+
|
|
41
|
+
### Changed — pcap-doctor
|
|
42
|
+
|
|
43
|
+
- The npm launcher is published with npm trusted publishing (OIDC) instead of an `NPM_TOKEN` secret;
|
|
44
|
+
the release job skips with a notice when the package is not on npm yet (its first publish is
|
|
45
|
+
manual) or the version is already published (#93).
|
|
46
|
+
|
|
47
|
+
### Fixed — pcap-doctor
|
|
48
|
+
|
|
49
|
+
- RFC citations in TLS version and cipher-policy text (#72).
|
|
50
|
+
- The tool version in reports now comes from the installed package instead of a hard-coded string (#65).
|
|
51
|
+
|
|
52
|
+
**MVP hardening since 0.1.0 (issues #1–#44):**
|
|
53
|
+
|
|
54
|
+
`report.json` `schema_version` is `1.4.0` (additive: `Flow.first_frame`, `Flow` burst statistics,
|
|
55
|
+
`RtpStream.first_frame`, `SshSession.offered_in`, `TelnetLogin`).
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- Telnet logins: passwords typed after a `Password:` prompt are reported as `CLEARTEXT_CREDENTIAL`
|
|
60
|
+
(length and frame only, never the text); Telnet sessions now raise `CLEARTEXT_SERVICE` (#37).
|
|
61
|
+
- `SSH_TERRAPIN_EXPOSED`: chacha20-poly1305, or an EtM MAC with a CBC cipher, offered without strict
|
|
62
|
+
key exchange on both sides (#24).
|
|
63
|
+
- FTP `PASS` arguments and LDAP simple-bind passwords are reported as `CLEARTEXT_CREDENTIAL` (#14).
|
|
64
|
+
|
|
65
|
+
### Fixed — secrets
|
|
66
|
+
|
|
67
|
+
- SNMP community strings and MySQL queries are redacted where the index builds service details (#1).
|
|
68
|
+
- A scheme-less `Authorization` value (bare token) no longer appears in a finding title or evidence (#15).
|
|
69
|
+
|
|
70
|
+
### Fixed — evidence and correctness
|
|
71
|
+
|
|
72
|
+
- Every finding cites a real frame: RTP, SYN scan, odd-port, beaconing and DNS resolver evidence no
|
|
73
|
+
longer cite frame 0 or a stream index (#4, #6); SSH evidence cites the KEXINIT, not the banner (#25).
|
|
74
|
+
- The server's SSH KEXINIT no longer overwrites the client's offer (#25).
|
|
75
|
+
- `BEACONING_SHAPE` measures regularity of gaps between bursts; the old check could never fail (#10).
|
|
76
|
+
- `SERVICE_ON_ODD_PORT` no longer reports a client's ephemeral port (#16).
|
|
77
|
+
- `DNS_EXTERNAL_RESOLVER` finds the resolver by port and covers IPv6 and all private ranges (#6);
|
|
78
|
+
`DNS_TUNNEL_SHAPE` names the querying host and no longer counts responses as queries (#18).
|
|
79
|
+
- `chacha20-poly1305@openssh.com` is no longer listed as a weak SSH cipher (#2).
|
|
80
|
+
- Invalid or wrong finding references replaced (e.g. `CWE- exfil`, RFC 8999 on TLS findings) (#3).
|
|
81
|
+
- The service field list is shared with the tshark pass; four names that are not tshark fields were removed (#14).
|
|
82
|
+
- `WELL_KNOWN_PORTS`: 506/522/1194 mapped to the wrong service (#23).
|
|
83
|
+
|
|
84
|
+
### Changed — docs
|
|
85
|
+
|
|
86
|
+
- README redesigned CLI-first: light/dark logo, badges, a numbered get-started path including a CI
|
|
87
|
+
gate, a table of contents, exit codes, and a privacy/telemetry section; the documented default
|
|
88
|
+
output folder is corrected to `<capture>.pf-report/` (#41).
|
|
89
|
+
- CONTRIBUTING gains how-to-start, pull-request and other-ways-to-help sections; a pull request
|
|
90
|
+
template follows the AGENTS.md contract (#41).
|
|
91
|
+
|
|
92
|
+
### Fixed — project
|
|
93
|
+
|
|
94
|
+
- Mermaid: `04-diagrams.md` emitted an invalid RTP gantt for captures without RTP (28 of 179 generated
|
|
95
|
+
diagrams failed to render), and the README's sequence diagram broke on a `;` (#43).
|
|
96
|
+
- CI could never pass (non-existent tshark preference, fixture drift check on randomly captured TLS
|
|
97
|
+
fixtures); it now runs the full suite (#12).
|
|
98
|
+
|
|
99
|
+
## [0.1.0] — 2026-09-29
|
|
100
|
+
|
|
101
|
+
First working release. `report.json` `schema_version` is `1.0.0`.
|
|
102
|
+
|
|
103
|
+
### Added — parsing
|
|
104
|
+
|
|
105
|
+
- `TsharkRunner`: 10 pinned tshark passes, `-T fields` with a documented
|
|
106
|
+
separator/aggregator, content-addressed disk cache keyed by capture SHA-256,
|
|
107
|
+
tshark version and pass arguments.
|
|
108
|
+
- Field and display-filter drift handling: fields validated against
|
|
109
|
+
`tshark -G fields`, protocol tokens against `tshark -G protocols`, preferences
|
|
110
|
+
against `tshark -G defaultprefs`; every drop recorded as a report note.
|
|
111
|
+
- `CaptureIndex`: flows, hosts, TLS/DTLS sessions, HTTP exchanges, DNS queries,
|
|
112
|
+
SIP messages with SDP, RTP streams, SSH sessions, QUIC sessions, and other
|
|
113
|
+
recognised services.
|
|
114
|
+
- SDP-driven RTP decode-as, so media on dynamic ports is dissected at all.
|
|
115
|
+
- Certificate enrichment through `openssl x509`, with an explicit
|
|
116
|
+
`name_source` field and a tshark-flattened fallback.
|
|
117
|
+
- Cipher registry of 424 suites derived from tshark's own value table, with
|
|
118
|
+
weakness tags and forward-secrecy reasoning derived from the IANA name.
|
|
119
|
+
|
|
120
|
+
### Added — detectors
|
|
121
|
+
|
|
122
|
+
- `d1.tls_cipher`: deprecated versions, weak/prohibited suites, offered-but-unused
|
|
123
|
+
weak suites, missing forward secrecy, certificate expiry/key size/signature,
|
|
124
|
+
self-signed leaf, alerts, truncated handshakes, JA3 fleet clustering.
|
|
125
|
+
- `d2.transport_exposure`: cleartext HTTP auth, Basic auth inside TLS, cookies
|
|
126
|
+
without `Secure`, cleartext services, leaked credentials, services on odd
|
|
127
|
+
ports, unanswered-SYN scan shape, beaconing shape.
|
|
128
|
+
- `d3.sip_rtp`: SIP call graph, cleartext signalling with auth headers, SDP
|
|
129
|
+
without `a=crypto`, unprotected RTP, media volume anomalies.
|
|
130
|
+
- `d4.dns_quic_ssh`: plaintext DNS leakage, DNS tunnelling heuristics, external
|
|
131
|
+
resolvers, QUIC version inventory and payload opacity, weak SSH negotiation.
|
|
132
|
+
|
|
133
|
+
### Added — output
|
|
134
|
+
|
|
135
|
+
- Six artifacts per run: `index.md`, `01-flows.md`, `02-ciphers.md`,
|
|
136
|
+
`03-findings.md`, `04-diagrams.md`, `report.json`.
|
|
137
|
+
- Mermaid topology, crypto matrix, severity pie, per-detector bar chart, RTP
|
|
138
|
+
timeline and SIP sequence diagrams, all total functions.
|
|
139
|
+
- Stable finding ids (`detector.code.sha256(scope)[:16]`) so reports diff.
|
|
140
|
+
- `pf` CLI: `analyze`, `flows`, `ciphers`, `detectors`, `suites`, `doctor`,
|
|
141
|
+
`schema`, with `--fail-on` for CI gates.
|
|
142
|
+
|
|
143
|
+
### Added — safety
|
|
144
|
+
|
|
145
|
+
- Credential redaction at the model boundary, with tests that grep the artifacts
|
|
146
|
+
for the original material.
|
|
147
|
+
- One detector raising an exception cannot sink a run; it is reported as a note.
|
|
148
|
+
- Unknown cipher ids, unparseable certificates and missing tshark fields all
|
|
149
|
+
produce findings or notes, never silent passes.
|
|
150
|
+
|
|
151
|
+
### Added — engineering
|
|
152
|
+
|
|
153
|
+
- 85 tests across four layers: registry, tshark boundary, detectors with
|
|
154
|
+
synthetic fixtures, and regression assertions over the Wireshark sample
|
|
155
|
+
corpus checked by hand against `tshark -V`.
|
|
156
|
+
- Fixtures generated deterministically; TLS fixtures captured from genuine
|
|
157
|
+
OpenSSL handshakes through a logging proxy.
|
|
158
|
+
- `ruff`, `mypy --strict` and `pytest` are the CI gate (`make verify`).
|
|
159
|
+
- `AGENTS.md` and `docs/subagent-playbook.md`: the subagent isolation contract
|
|
160
|
+
and a 54-item seed issue board (`docs/issue-board.md`).
|
|
161
|
+
|
|
162
|
+
### Known limits
|
|
163
|
+
|
|
164
|
+
- Encrypted payloads (QUIC, TLS 1.3 application data) are opaque without a keylog.
|
|
165
|
+
- Certificate subject/issuer require openssl; without it the report says so.
|
|
166
|
+
- DNS tunnelling and beaconing are heuristics, reported at low confidence.
|
|
167
|
+
- Server/client orientation uses port heuristics; odd ports are flagged instead
|
|
168
|
+
of trusted.
|