ja4plus 1.2.2__tar.gz → 1.3.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.
Files changed (525) hide show
  1. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/rules/conformance.md +4 -3
  2. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/rules/external-apis.md +18 -11
  3. {ja4plus-1.2.2 → ja4plus-1.3.0}/CHANGELOG.md +70 -0
  4. {ja4plus-1.2.2 → ja4plus-1.3.0}/CLAUDE.md +1 -1
  5. {ja4plus-1.2.2 → ja4plus-1.3.0}/PKG-INFO +17 -12
  6. {ja4plus-1.2.2 → ja4plus-1.3.0}/README.md +15 -11
  7. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/api_reference.md +63 -8
  8. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/implementation_notes.md +16 -10
  9. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/index.md +20 -9
  10. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4.md +3 -3
  11. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4t.md +7 -3
  12. ja4plus-1.3.0/docs/methods/ja4tscan.md +141 -0
  13. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/migration-0.6-to-1.0.md +15 -13
  14. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/output-schema.md +12 -5
  15. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/performance.md +6 -1
  16. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/00-foundation.md +3 -2
  17. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/01-spec-conformance.md +15 -5
  18. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/03-concurrency-safety.md +14 -12
  19. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/08-documentation.md +8 -4
  20. ja4plus-1.3.0/docs/specs/features/12-active-scan.md +406 -0
  21. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4.md +2 -2
  22. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4L.md +6 -6
  23. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4SSH.md +1 -1
  24. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4T.md +28 -3
  25. ja4plus-1.3.0/docs/specs/foxio/JA4TScan.md +408 -0
  26. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4X.md +2 -2
  27. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/README.md +11 -4
  28. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/deleted-text-specifications.md +1 -1
  29. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/zeek.md +11 -3
  30. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/spec.html +9 -4
  31. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/spec.md +61 -19
  32. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/usage.md +28 -1
  33. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/__init__.py +1 -1
  34. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/cli.py +128 -0
  35. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4.py +140 -6
  36. ja4plus-1.3.0/ja4plus/scan/__init__.py +11 -0
  37. ja4plus-1.3.0/ja4plus/scan/command.py +192 -0
  38. ja4plus-1.3.0/ja4plus/scan/frames.py +198 -0
  39. ja4plus-1.3.0/ja4plus/scan/link.py +183 -0
  40. ja4plus-1.3.0/ja4plus/scan/scanner.py +404 -0
  41. ja4plus-1.3.0/ja4plus/scan/value.py +83 -0
  42. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/types.py +3 -2
  43. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/tcp_options.py +29 -12
  44. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/tls_utils.py +40 -0
  45. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus.egg-info/PKG-INFO +17 -12
  46. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus.egg-info/SOURCES.txt +22 -0
  47. ja4plus-1.3.0/ja4plus.egg-info/entry_points.txt +5 -0
  48. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus.egg-info/requires.txt +2 -0
  49. {ja4plus-1.2.2 → ja4plus-1.3.0}/mkdocs.yml +3 -2
  50. {ja4plus-1.2.2 → ja4plus-1.3.0}/pyproject.toml +13 -0
  51. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/download_test_vectors.py +31 -6
  52. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_citation_lines.py +11 -1
  53. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_deviations.json +0 -6
  54. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_deviations.py +1 -1
  55. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vector_manifest.json +1 -0
  56. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/NOTICE +18 -7
  57. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@chrome-cloudflare-quic-with-secrets.pcapng.snap +1 -2
  58. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@gre-erspan-vxlan.pcap.snap +1 -2
  59. ja4plus-1.3.0/tests/foxio_vectors/rust_expected/ja4__insta@sigalg-grease.pcapng.snap +16 -0
  60. ja4plus-1.3.0/tests/foxio_vectors/sigalg-grease.pcapng +0 -0
  61. ja4plus-1.3.0/tests/foxio_vectors/sigalg-grease.pcapng.json +20 -0
  62. ja4plus-1.3.0/tests/scan_fakes.py +95 -0
  63. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_conformance_frame_discrimination.py +4 -4
  64. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_image_count.py +4 -3
  65. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documented_method_count.py +71 -9
  66. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documented_state_table_count.py +1 -1
  67. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_download_filter_fields.py +3 -3
  68. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_empty_list_sentinel_ruling.py +4 -4
  69. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_rust_parity.py +29 -29
  70. ja4plus-1.3.0/tests/test_ja4_signature_algorithm_grease.py +127 -0
  71. ja4plus-1.3.0/tests/test_ja4_tcp_client_hello_reassembly.py +328 -0
  72. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4s_raw_order.py +4 -4
  73. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t_form.py +48 -17
  74. ja4plus-1.3.0/tests/test_ja4tscan_boundary.py +245 -0
  75. ja4plus-1.3.0/tests/test_ja4tscan_command.py +476 -0
  76. ja4plus-1.3.0/tests/test_ja4tscan_frames.py +196 -0
  77. ja4plus-1.3.0/tests/test_ja4tscan_link.py +254 -0
  78. ja4plus-1.3.0/tests/test_ja4tscan_scanner.py +390 -0
  79. ja4plus-1.3.0/tests/test_ja4tscan_specification.py +119 -0
  80. ja4plus-1.3.0/tests/test_ja4tscan_value.py +227 -0
  81. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_key_log_interface_ruling.py +1 -1
  82. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_memory_bounds.py +10 -9
  83. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_method_pages.py +45 -35
  84. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_precedence_exception.py +54 -29
  85. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_processor_stats.py +4 -4
  86. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_readme_contracts.py +16 -15
  87. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_tcp_segment_bound_ruling.py +2 -2
  88. ja4plus-1.2.2/ja4plus.egg-info/entry_points.txt +0 -2
  89. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/rules/agent-reports.md +0 -0
  90. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/rules/batch-gate.md +0 -0
  91. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/rules/ste.md +0 -0
  92. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/settings.json +0 -0
  93. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/skills/conformance/SKILL.md +0 -0
  94. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/skills/release/SKILL.md +0 -0
  95. {ja4plus-1.2.2 → ja4plus-1.3.0}/.claude/skills/test/SKILL.md +0 -0
  96. {ja4plus-1.2.2 → ja4plus-1.3.0}/.git-blame-ignore-revs +0 -0
  97. {ja4plus-1.2.2 → ja4plus-1.3.0}/.github/dependabot.yml +0 -0
  98. {ja4plus-1.2.2 → ja4plus-1.3.0}/.github/workflows/docs-build.yml +0 -0
  99. {ja4plus-1.2.2 → ja4plus-1.3.0}/.github/workflows/docs.yml +0 -0
  100. {ja4plus-1.2.2 → ja4plus-1.3.0}/.github/workflows/publish.yml +0 -0
  101. {ja4plus-1.2.2 → ja4plus-1.3.0}/.github/workflows/test.yml +0 -0
  102. {ja4plus-1.2.2 → ja4plus-1.3.0}/.github/workflows/update-db.yml +0 -0
  103. {ja4plus-1.2.2 → ja4plus-1.3.0}/.gitignore +0 -0
  104. {ja4plus-1.2.2 → ja4plus-1.3.0}/.issue-flow.json +0 -0
  105. {ja4plus-1.2.2 → ja4plus-1.3.0}/LICENSE +0 -0
  106. {ja4plus-1.2.2 → ja4plus-1.3.0}/assets/logo.png +0 -0
  107. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/README.md +0 -0
  108. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/concurrency.md +0 -0
  109. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4d.md +0 -0
  110. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4d6.md +0 -0
  111. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4h.md +0 -0
  112. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4l.md +0 -0
  113. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4ls.md +0 -0
  114. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4s.md +0 -0
  115. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4ssh.md +0 -0
  116. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4ts.md +0 -0
  117. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/methods/ja4x.md +0 -0
  118. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_reports/412-utils.json +0 -0
  119. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_reports/413-fingerprinters.json +0 -0
  120. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_reports/414-interface.json +0 -0
  121. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_settlements/412-utils.json +0 -0
  122. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_settlements/413-fingerprinters.json +0 -0
  123. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_settlements/414-interface.json +0 -0
  124. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/mutation_sweep.md +0 -0
  125. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/reference/fingerprinters.md +0 -0
  126. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/reference/index.md +0 -0
  127. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/reference/ja4db.md +0 -0
  128. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/reference/processor.md +0 -0
  129. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/reference/types.md +0 -0
  130. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/02-correctness-audit.md +0 -0
  131. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/04-typed-api.md +0 -0
  132. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/05-structured-output.md +0 -0
  133. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/06-live-capture.md +0 -0
  134. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/07-db-enrichment.md +0 -0
  135. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/09-release.md +0 -0
  136. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/features/11-pre-release-validation.md +0 -0
  137. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4D.md +0 -0
  138. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4H.md +0 -0
  139. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/foxio/JA4S.md +0 -0
  140. {ja4plus-1.2.2 → ja4plus-1.3.0}/docs/specs/mockups/01-cli-output.html +0 -0
  141. {ja4plus-1.2.2 → ja4plus-1.3.0}/examples/demo_fingerprints.py +0 -0
  142. {ja4plus-1.2.2 → ja4plus-1.3.0}/examples/live_traffic_fingerprinting.py +0 -0
  143. {ja4plus-1.2.2 → ja4plus-1.3.0}/examples/pcap_analysis.py +0 -0
  144. {ja4plus-1.2.2 → ja4plus-1.3.0}/examples/tcpdump_workflows.py +0 -0
  145. {ja4plus-1.2.2 → ja4plus-1.3.0}/examples/threat_detection.py +0 -0
  146. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/data/ja4plus-mapping.csv +0 -0
  147. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/__init__.py +0 -0
  148. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/base.py +0 -0
  149. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4d.py +0 -0
  150. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4d6.py +0 -0
  151. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4h.py +0 -0
  152. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4l.py +0 -0
  153. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4s.py +0 -0
  154. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4ssh.py +0 -0
  155. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4t.py +0 -0
  156. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4ts.py +0 -0
  157. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/fingerprinters/ja4x.py +0 -0
  158. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/ja4db.py +0 -0
  159. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/output.py +0 -0
  160. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/processor.py +0 -0
  161. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/py.typed +0 -0
  162. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/__init__.py +0 -0
  163. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/http_utils.py +0 -0
  164. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/icmp_quoted.py +0 -0
  165. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/loopback.py +0 -0
  166. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/packet_utils.py +0 -0
  167. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/quic_utils.py +0 -0
  168. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/ssh_utils.py +0 -0
  169. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/state_table.py +0 -0
  170. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/tcp_stream.py +0 -0
  171. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/tunnels.py +0 -0
  172. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/utils/x509_utils.py +0 -0
  173. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus/watch.py +0 -0
  174. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus.egg-info/dependency_links.txt +0 -0
  175. {ja4plus-1.2.2 → ja4plus-1.3.0}/ja4plus.egg-info/top_level.txt +0 -0
  176. {ja4plus-1.2.2 → ja4plus-1.3.0}/setup.cfg +0 -0
  177. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/__init__.py +0 -0
  178. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/batch_gate.py +0 -0
  179. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/build_alpn_condition_capture.py +0 -0
  180. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/build_ja4ts_rst.py +0 -0
  181. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/build_quic_ja4l_captures.py +0 -0
  182. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/build_ssh_retransmission.py +0 -0
  183. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/citation_line_allowlist.json +0 -0
  184. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/compare_zeek_baselines.py +0 -0
  185. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/conformance_index.py +0 -0
  186. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/conftest.py +0 -0
  187. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/dns.cap +0 -0
  188. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/example_cert.der +0 -0
  189. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/filedownload.dmp +0 -0
  190. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/http.cap +0 -0
  191. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/suspectemployeechat.dmp +0 -0
  192. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/test_cert.der +0 -0
  193. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/data/wrongdissector.dmp +0 -0
  194. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/dependency_entries.py +0 -0
  195. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/documentation_samples.py +0 -0
  196. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_deviation_owners.json +0 -0
  197. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_manifest.py +0 -0
  198. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/CVE-2018-6794.pcap +0 -0
  199. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/CVE-2018-6794.pcap.json +0 -0
  200. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/alpn-condition.pcap +0 -0
  201. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/alpn-condition.pcap.json +0 -0
  202. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/badcurveball.pcap +0 -0
  203. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/badcurveball.pcap.json +0 -0
  204. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/browsers-x509.pcapng +0 -0
  205. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/browsers-x509.pcapng.json +0 -0
  206. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/chrome-cloudflare-quic-with-secrets.pcapng +0 -0
  207. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/chrome-cloudflare-quic-with-secrets.pcapng.json +0 -0
  208. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/dhcp.pcapng +0 -0
  209. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/dhcp.pcapng.json +0 -0
  210. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/dhcpv6.pcap +0 -0
  211. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/dhcpv6.pcap.json +0 -0
  212. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/gre-erspan-vxlan.pcap +0 -0
  213. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/gre-erspan-vxlan.pcap.json +0 -0
  214. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/gre-sample.pcap +0 -0
  215. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/gre-sample.pcap.json +0 -0
  216. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http-empty-useragent.pcap +0 -0
  217. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http-empty-useragent.pcap.json +0 -0
  218. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http1-with-cookies.pcapng +0 -0
  219. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http1-with-cookies.pcapng.json +0 -0
  220. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http1.pcapng +0 -0
  221. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http1.pcapng.json +0 -0
  222. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http2-with-cookies.pcapng +0 -0
  223. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/http2-with-cookies.pcapng.json +0 -0
  224. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/https-connect.pcap +0 -0
  225. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/https-connect.pcap.json +0 -0
  226. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/https3-301-get.pcap +0 -0
  227. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/https3-301-get.pcap.json +0 -0
  228. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ipv6.pcapng +0 -0
  229. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ipv6.pcapng.json +0 -0
  230. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/latest.pcapng +0 -0
  231. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/latest.pcapng.json +0 -0
  232. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/macos_tcp_flags.pcap +0 -0
  233. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/macos_tcp_flags.pcap.json +0 -0
  234. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/quic-tls-handshake.pcapng +0 -0
  235. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/quic-tls-handshake.pcapng.json +0 -0
  236. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/quic-with-several-tls-frames.pcapng +0 -0
  237. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/quic-with-several-tls-frames.pcapng.json +0 -0
  238. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@browsers-x509.pcapng.snap +0 -0
  239. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@https-connect.pcap.snap +0 -0
  240. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@latest.pcapng.snap +0 -0
  241. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@quic-tls-handshake.pcapng.snap +0 -0
  242. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@quic-with-several-tls-frames.pcapng.snap +0 -0
  243. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@ssh2.pcapng.snap +0 -0
  244. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@tls-handshake.pcapng.snap +0 -0
  245. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@tls-sni.pcapng.snap +0 -0
  246. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/rust_expected/ja4__insta@tls3.pcapng.snap +0 -0
  247. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/single-packets.pcap +0 -0
  248. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/single-packets.pcap.json +0 -0
  249. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/socks-https-example.pcap +0 -0
  250. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/socks-https-example.pcap.json +0 -0
  251. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/socks4-https.pcap +0 -0
  252. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/socks4-https.pcap.json +0 -0
  253. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh-r.pcap +0 -0
  254. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh-r.pcap.json +0 -0
  255. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh-scp-1050.pcap +0 -0
  256. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh-scp-1050.pcap.json +0 -0
  257. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh.pcapng +0 -0
  258. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh.pcapng.json +0 -0
  259. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh2-malformed.pcap +0 -0
  260. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh2-malformed.pcap.json +0 -0
  261. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh2-moloch-crash.pcap +0 -0
  262. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh2-moloch-crash.pcap.json +0 -0
  263. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh2.pcapng +0 -0
  264. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/ssh2.pcapng.json +0 -0
  265. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/sshv1.pcap +0 -0
  266. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/sshv1.pcap.json +0 -0
  267. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tcpdump-geneve.pcap +0 -0
  268. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tcpdump-geneve.pcap.json +0 -0
  269. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-alpn-h2.pcap +0 -0
  270. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-alpn-h2.pcap.json +0 -0
  271. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-handshake.pcapng +0 -0
  272. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-handshake.pcapng.json +0 -0
  273. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-non-ascii-alpn.pcapng +0 -0
  274. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-non-ascii-alpn.pcapng.json +0 -0
  275. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-sni.pcapng +0 -0
  276. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls-sni.pcapng.json +0 -0
  277. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls12.pcap +0 -0
  278. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls12.pcap.json +0 -0
  279. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls3.pcapng +0 -0
  280. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/tls3.pcapng.json +0 -0
  281. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/v6.pcap +0 -0
  282. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/v6.pcap.json +0 -0
  283. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/CVE-2018-6794.pcap.json +0 -0
  284. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/badcurveball.pcap.json +0 -0
  285. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/browsers-x509.pcapng.json +0 -0
  286. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/chrome-cloudflare-quic-with-secrets.pcapng.json +0 -0
  287. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/dhcp.pcapng.json +0 -0
  288. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/dhcpv6.pcap.json +0 -0
  289. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/gre-erspan-vxlan.pcap.json +0 -0
  290. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/http-empty-useragent.pcap.json +0 -0
  291. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/http1-with-cookies.pcapng.json +0 -0
  292. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/http2-with-cookies.pcapng.json +0 -0
  293. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/https-connect.pcap.json +0 -0
  294. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/https3-301-get.pcap.json +0 -0
  295. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/ipv6.pcapng.json +0 -0
  296. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/latest.pcapng.json +0 -0
  297. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/socks-https-example.pcap.json +0 -0
  298. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/socks4-https.pcap.json +0 -0
  299. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/ssh-r.pcap.json +0 -0
  300. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/ssh-scp-1050.pcap.json +0 -0
  301. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/ssh2-malformed.pcap.json +0 -0
  302. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/ssh2-moloch-crash.pcap.json +0 -0
  303. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/ssh2.pcapng.json +0 -0
  304. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/sshv1.pcap.json +0 -0
  305. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/tcpdump-geneve.pcap.json +0 -0
  306. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/tls-alpn-h2.pcap.json +0 -0
  307. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/tls3.pcapng.json +0 -0
  308. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/wireshark_expected/v6.pcap.json +0 -0
  309. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-conn/conn.log +0 -0
  310. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-conn-quic/conn.log +0 -0
  311. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-conn-tls3/conn.log +0 -0
  312. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-dhcp/ja4d.log +0 -0
  313. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-http1-with-cookies/http.log +0 -0
  314. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-ssh2/ja4ssh.log +0 -0
  315. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/foxio_vectors/zeek_expected/Scripts.ja4-tls-handshake/ssl.log +0 -0
  316. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/README.md +0 -0
  317. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/__init__.py +0 -0
  318. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/conftest.py +0 -0
  319. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/support.py +0 -0
  320. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_icmp_quoted_header.py +0 -0
  321. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_length_fields.py +0 -0
  322. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_malformed_captures.py +0 -0
  323. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_server_hello_extensions.py +0 -0
  324. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_structural_validity.py +0 -0
  325. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_synthetic_hostile_input.py +0 -0
  326. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/fuzz/test_truncated_captures.py +0 -0
  327. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/generate_foxio_baseline.py +0 -0
  328. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/measure_random_client_hello.py +0 -0
  329. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/memory_ceiling_run.py +0 -0
  330. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/mutation_census.py +0 -0
  331. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/mutation_cover.py +0 -0
  332. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/mutation_sweep.py +0 -0
  333. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/quic_builder.py +0 -0
  334. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/release_body.py +0 -0
  335. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/release_verification.py +0 -0
  336. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/skip_gate.py +0 -0
  337. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/state_readers.py +0 -0
  338. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_agent_report_absence_rule.py +0 -0
  339. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_alpn_ruling_register.py +0 -0
  340. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_api_reference_interface_table.py +0 -0
  341. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_bare_exception_reading.py +0 -0
  342. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_batch_gate.py +0 -0
  343. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_batch_gate_protection_rule.py +0 -0
  344. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_bounded_state_table_adoption.py +0 -0
  345. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_breaking_change_record.py +0 -0
  346. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_changelog_round_agreement.py +0 -0
  347. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_changelog_sentence_exemption.py +0 -0
  348. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cleanup_connection.py +0 -0
  349. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cli.py +0 -0
  350. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cli_closed_window_endpoints.py +0 -0
  351. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cli_lookup_opt_in.py +0 -0
  352. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cli_output_option.py +0 -0
  353. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cli_processor.py +0 -0
  354. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_cli_types_ja4ls.py +0 -0
  355. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_compare_zeek_baselines.py +0 -0
  356. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_comprehensive.py +0 -0
  357. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_criterion_counts.py +0 -0
  358. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_db_cache_directory.py +0 -0
  359. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_db_item_access.py +0 -0
  360. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_db_lookup_many.py +0 -0
  361. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_db_offline.py +0 -0
  362. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_decryption_non_goal_ruling.py +0 -0
  363. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_delegated_ruling_rule.py +0 -0
  364. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_dependency_entries.py +0 -0
  365. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_dhcp_tunnel_inner_udp.py +0 -0
  366. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_behaviour_rule.py +0 -0
  367. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_fallback_rule.py +0 -0
  368. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_publish.py +0 -0
  369. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_samples.py +0 -0
  370. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_site.py +0 -0
  371. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_documentation_version_claims.py +0 -0
  372. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_edge_cases.py +0 -0
  373. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_examples.py +0 -0
  374. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_citation_lines.py +0 -0
  375. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_deviations.py +0 -0
  376. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_ja4_transcription.py +0 -0
  377. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_license_register.py +0 -0
  378. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_manifest.py +0 -0
  379. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_wireshark_ja4ts.py +0 -0
  380. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_foxio_zeek_ja4ts.py +0 -0
  381. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_future_dated_packet_eviction.py +0 -0
  382. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_generate_foxio_baseline.py +0 -0
  383. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_get_stream_byte_cap_truncation.py +0 -0
  384. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_http_header_block_terminator.py +0 -0
  385. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_installed_wheel.py +0 -0
  386. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_installed_wheel_selection.py +0 -0
  387. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_integration.py +0 -0
  388. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_interpreter_set.py +0 -0
  389. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ipv6_support.py +0 -0
  390. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4.py +0 -0
  391. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_alpn.py +0 -0
  392. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_alpn_condition.py +0 -0
  393. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_deep.py +0 -0
  394. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_empty_ext.py +0 -0
  395. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_hello_retry.py +0 -0
  396. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_original_order.py +0 -0
  397. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_raw_branches.py +0 -0
  398. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4_ssl2_version.py +0 -0
  399. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4d.py +0 -0
  400. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4d6_foxio.py +0 -0
  401. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4d_decisions.py +0 -0
  402. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4d_foxio.py +0 -0
  403. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4d_fqdn_name_ruling.py +0 -0
  404. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4db.py +0 -0
  405. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4db_empty_list_alias.py +0 -0
  406. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h.py +0 -0
  407. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_cookie_list.py +0 -0
  408. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_deep.py +0 -0
  409. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_emission_frame_ruling.py +0 -0
  410. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_large_cookie_header.py +0 -0
  411. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_line_endings.py +0 -0
  412. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_part_a_readings.py +0 -0
  413. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_raw.py +0 -0
  414. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_raw_sorted.py +0 -0
  415. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_reassembly.py +0 -0
  416. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_request_line_and_empty_header_list.py +0 -0
  417. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_retransmission.py +0 -0
  418. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4h_spec.py +0 -0
  419. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l.py +0 -0
  420. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_connection_state.py +0 -0
  421. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_foxio.py +0 -0
  422. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_header_block_terminator.py +0 -0
  423. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_quic_eviction_ruling.py +0 -0
  424. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_quic_marker.py +0 -0
  425. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_quic_point_b_ruling.py +0 -0
  426. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_quic_point_d_emission.py +0 -0
  427. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_quic_repeated_connection.py +0 -0
  428. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_quic_server_hello.py +0 -0
  429. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_return_path.py +0 -0
  430. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_timing_fork_ruling.py +0 -0
  431. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4l_udp_direction.py +0 -0
  432. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4s.py +0 -0
  433. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4s_quic_direction.py +0 -0
  434. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4s_quic_split_server_hello.py +0 -0
  435. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh.py +0 -0
  436. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_close_connection_window.py +0 -0
  437. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_deep.py +0 -0
  438. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_direction.py +0 -0
  439. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_interpret.py +0 -0
  440. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_message_count.py +0 -0
  441. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_packet_selection_ruling.py +0 -0
  442. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_parity_record.py +0 -0
  443. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_server_side.py +0 -0
  444. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_spec.py +0 -0
  445. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_window.py +0 -0
  446. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ssh_windows.py +0 -0
  447. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t.py +0 -0
  448. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t_citation_lines.py +0 -0
  449. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t_deep.py +0 -0
  450. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t_emission_model_ruling.py +0 -0
  451. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t_icmp_quoted.py +0 -0
  452. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4t_syn_selection_ruling.py +0 -0
  453. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ts.py +0 -0
  454. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ts_delay_rounding_ruling.py +0 -0
  455. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ts_part_e.py +0 -0
  456. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ts_prefix_bound.py +0 -0
  457. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4ts_reset.py +0 -0
  458. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x.py +0 -0
  459. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x_deep.py +0 -0
  460. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x_empty_ext.py +0 -0
  461. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x_named_exceptions.py +0 -0
  462. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x_raw.py +0 -0
  463. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x_reassembly.py +0 -0
  464. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ja4x_record_scan.py +0 -0
  465. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_lint_gate_pin.py +0 -0
  466. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_loopback_link_type.py +0 -0
  467. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_mutation_census.py +0 -0
  468. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_mutation_cover.py +0 -0
  469. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_mutation_sweep_annotation_skip.py +0 -0
  470. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_mutation_sweep_commit.py +0 -0
  471. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_mutation_sweep_module_list.py +0 -0
  472. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_mutation_sweep_timeout.py +0 -0
  473. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_no_retained_packets.py +0 -0
  474. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_output_schema.py +0 -0
  475. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_packaging.py +0 -0
  476. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_packet_utils.py +0 -0
  477. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_parity.py +0 -0
  478. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_parser_description.py +0 -0
  479. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ported_pattern_cost.py +0 -0
  480. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_processor.py +0 -0
  481. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_processor_typed_results.py +0 -0
  482. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_public_interface.py +0 -0
  483. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_publish_workflow.py +0 -0
  484. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_coalesced_datagram_ruling.py +0 -0
  485. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_crypto_buffer_bound.py +0 -0
  486. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_crypto_gap.py +0 -0
  487. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_integration.py +0 -0
  488. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_multipacket.py +0 -0
  489. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_server_initial.py +0 -0
  490. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_tunnel_inner_udp.py +0 -0
  491. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_quic_utils.py +0 -0
  492. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_release_body.py +0 -0
  493. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_release_notes.py +0 -0
  494. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_release_skill.py +0 -0
  495. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_requirement_scope.py +0 -0
  496. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_round_entry_existence.py +0 -0
  497. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_ruling_vocabulary.py +0 -0
  498. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_settlement_procedure.py +0 -0
  499. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_skip_gate.py +0 -0
  500. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_spec_validation.py +0 -0
  501. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_specification_changelog.py +0 -0
  502. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_specification_terms.py +0 -0
  503. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_state_table.py +0 -0
  504. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_statistics_thread_term.py +0 -0
  505. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_tcp_byte_cap_admission_ruling.py +0 -0
  506. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_tcp_stream.py +0 -0
  507. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_thread_safety.py +0 -0
  508. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_throughput.py +0 -0
  509. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_tls_utils.py +0 -0
  510. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_tunnels.py +0 -0
  511. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_types.py +0 -0
  512. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_us_english_spelling.py +0 -0
  513. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_utils.py +0 -0
  514. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_version_gate.py +0 -0
  515. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_watch.py +0 -0
  516. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_watch_capture.py +0 -0
  517. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_watch_drop_count.py +0 -0
  518. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_watch_statistics.py +0 -0
  519. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_watch_stop.py +0 -0
  520. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_wireshark_expected_counts.py +0 -0
  521. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_workflow_action_pins.py +0 -0
  522. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/test_x509_certificate_reader.py +0 -0
  523. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/throughput_run.py +0 -0
  524. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/universal_skips.json +0 -0
  525. {ja4plus-1.2.2 → ja4plus-1.3.0}/tests/version_gate.py +0 -0
@@ -88,11 +88,12 @@ and under the restored defect. These are the seven moves.
88
88
 
89
89
  **The conformance value comparison reads the one source that frames nothing.** A per-frame
90
90
  comparison is possible only where a source states the frame, and #736 read all four.
91
+ #772 added `sigalg-grease.pcapng`, and the first two counts below include it.
91
92
 
92
93
  | Source | States a frame | Values |
93
94
  |---|---|---|
94
- | FoxIO Python expected output, `tests/foxio_vectors/*.json` | No | 1203 |
95
- | FoxIO Rust snapshots, `tests/foxio_vectors/rust_expected/*.snap` | No | 460 |
95
+ | FoxIO Python expected output, `tests/foxio_vectors/*.json` | No | 1211 |
96
+ | FoxIO Rust snapshots, `tests/foxio_vectors/rust_expected/*.snap` | No | 465 |
96
97
  | Zeek baselines, `tests/foxio_vectors/zeek_expected/` | No | 7 baselines |
97
98
  | FoxIO Wireshark dissector, `tests/foxio_vectors/wireshark_expected/*.json` | Yes, `frame.number` | 724 |
98
99
 
@@ -101,7 +102,7 @@ comparison is possible only where a source states the frame, and #736 read all f
101
102
  names one of them. **The dissector frames every value it writes**, and #736 measured 724
102
103
  framed values and none unframed across 26 files.
103
104
 
104
- **The conformance suite builds all 1203 of its value cases from the first source.** No case
105
+ **The conformance suite builds all 1211 of its value cases from the first source.** No case
105
106
  of the suite can read a frame, because the source it reads states none.
106
107
 
107
108
  ### Why the suite compares no frame
@@ -153,10 +153,12 @@ reader checks each one.
153
153
  5. The remaining sources hold one value between them.
154
154
 
155
155
  **Read fact 1 from the vectors, and not from the key form alone.** A value-form key
156
- states that the Python file holds a value, and on one row that statement is false.
157
- `gre-erspan-vxlan.pcap/0:65174/JA4T.1` carries the value form, the FoxIO Python file
158
- holds no JA4T value, and #215 declines the FoxIO Rust value `8192__0_0` rather than a
159
- Python one. The exception rests on a declined Python value, so it passes over that row.
156
+ states that the Python file holds a value, and that statement can be false. Such a row
157
+ declines the value of another source, and the exception passes over it, because the
158
+ exception rests on a declined Python value. `gre-erspan-vxlan.pcap/0:65174/JA4T.1` was
159
+ that row until #772. The FoxIO Python file holds no JA4T value, and #215 declined the
160
+ FoxIO Rust value `8192__0_0`. The Rust snapshot at FoxIO commit `16b96d95` holds
161
+ `8192_00_00_00`, which `ja4plus` writes, so #772 removed the row.
160
162
 
161
163
  **A capability decline bars the row.** The exception reaches a row only where the decline
162
164
  records a disagreement about the value. It does not reach a row whose decline records a
@@ -179,9 +181,11 @@ prose of its cause.** Read the field.
179
181
  values of #129 out of `SOURCE_VALUES`, so the exclusion enforced the bar a second time.
180
182
  No case could then read what the field does on the one issue the bar exists for. The
181
183
  table now holds all 35, measured against the pinned commit. With `capability` false on
182
- every #129 entry the reach rises from 6 rows to 25. The same flip on the earlier table
183
- moved nothing. The 16 JA4H rows of the 35 stay out under the disagreement bar, because
184
- the Rust value and the Wireshark value differ on each one.
184
+ every #129 entry the reach rises from 6 rows to 41. The same flip on the earlier table
185
+ moved nothing. **#772 read the table again at FoxIO `16b96d95`, and the flip reached 25
186
+ rows before that read.** FoxIO `478359f3` moved the 16 Rust JA4H values, and each one now
187
+ equals the Wireshark value. The disagreement bar therefore holds none of the 35, and the
188
+ field alone bars all of them.
185
189
 
186
190
  **A disagreement between the remaining sources bars the row.** Where the remaining FoxIO
187
191
  sources hold different values, **no source holds the reference and the row stays declined
@@ -211,7 +215,7 @@ decides what the bar covers.
211
215
  reference value, and adoption is its own ruling. "Which baselines are usable as
212
216
  vectors" in `docs/specs/foxio/zeek.md` holds that ruling for the Zeek package.
213
217
 
214
- **The exception reaches 6 rows of the 138 the register holds.**
218
+ **The exception reaches 6 rows of the 137 the register holds.**
215
219
  `tests/test_precedence_exception.py` measures the reach and both counts, and #334 records
216
220
  the search. A case reads this sentence, so a register move fails the gate here.
217
221
 
@@ -224,9 +228,12 @@ the search. A case reads this sentence, so a register move fails the gate here.
224
228
  | `ssh2.pcapng/33:51810/JA4L-S.1` | #225 | Wireshark alone |
225
229
  | `tls3.pcapng/25:61884/JA4L-S.1` | #225 | Wireshark alone |
226
230
 
227
- **`FoxIO-LLC/ja4tscan` holds prose and no baseline.** Its `README.md` gives eight
228
- JA4TScan example values against named operating systems, and two of them record TCP
229
- option kind 0 inside the JA4T option list. #197 owns the scope ruling.
231
+ **`FoxIO-LLC/ja4tscan` holds source code and no baseline.** Its `README.md` gives eight
232
+ JA4TScan example values against named operating systems, and one of them records TCP
233
+ option kind 0 twice inside the option list. **`module_ja4tscan.c` is the normative
234
+ source**, and the maintainer named it so on 2026-09-30, in #775. Three of the eight
235
+ values do not fit that module. `docs/specs/foxio/JA4TScan.md` holds the transcription and
236
+ the measurement.
230
237
 
231
238
  **Warning: FoxIO states that `FoxIO-LLC/ja4-nginx-module` is not correct.** Its
232
239
  `README.md` opens with a `# NOTICE` section that reads "This version of JA4 has known
@@ -6,6 +6,37 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ - **`ja4plus scan` adds JA4TScan, the FoxIO active TCP server fingerprint** (#775, #776).
10
+ Round
11
+ 269. **The maintainer reversed the decline of 2026-08-08 on 2026-09-30.** The scanner sends
12
+ one SYN to each IPv4 target and reads the SYN-ACK and its retransmissions. Parts a to d
13
+ use the JA4TS form of this project, so a scan value and the passive JA4TS value of one
14
+ SYN-ACK hold the same four parts. A target whose first answer carries RST writes
15
+ `0_rst-ack`. **The library changes no firewall state.** The command prints the four
16
+ `iptables` rules of the FoxIO wrapper, or four pf rules on macOS, and it warns when a
17
+ SYN-ACK arrives with no retransmission. The scanner lives in `ja4plus/scan/` behind the
18
+ `scan` extra, and no passive module imports it. `docs/specs/foxio/JA4TScan.md` transcribes
19
+ `FoxIO-LLC/ja4tscan` at `d01bfec4`, and `docs/specs/features/12-active-scan.md` states 35
20
+ acceptance criteria. The divergence register records the three places where the FoxIO
21
+ module writes another form. **The project now implements all twelve FoxIO methods.**
22
+
23
+ - **JA4 reads a ClientHello that spans TCP segments, and the FoxIO pin moves to `16b96d95`** (#772, #773, #774).
24
+ Round
25
+ 268. **A post-quantum key share makes a browser ClientHello longer than one TCP segment**,
26
+ and the JA4 fingerprinter read each segment alone, so it wrote no value for such a hello.
27
+ `sigalg-grease.pcapng` of FoxIO release v1.0.1 holds a 2034-byte hello in two segments.
28
+ The fingerprinter now holds the client payload of a connection in a bounded table until
29
+ the hello is complete, and a gap stops the read at the first byte that has not arrived
30
+ (#772). **JA4 removes GREASE values from the signature algorithms** at both call sites, as
31
+ FoxIO `d66336ef` does, and the value of that capture now equals the FoxIO value in all
32
+ four forms (#773). **A repeated MSS option or window scale option keeps its last value**,
33
+ which the FoxIO Rust, Wireshark and Zeek sources each do at `16b96d95` (#774). The FoxIO
34
+ pin moves from `27f0cbf9` to `16b96d95`. The seven Zeek baselines stay at `27f0cbf9`
35
+ under `ZEEK_COMMIT`, because FoxIO `4e91886c` removed `zeek/tests/Traces`. **A replay of
36
+ 77 captures writes one new value, the JA4 of `sigalg-grease.pcapng` frame 5, and every
37
+ other value is byte-identical.** The conformance suite reports 1684 passed, 142 skipped
38
+ and 138 xfailed before this round, and 1702 passed, 145 skipped and 137 xfailed after it.
39
+
9
40
  - **The QUIC CRYPTO reader stops at the first byte that has not arrived** (#762).
10
41
  Round
11
42
  267. **An outside reporter measured the defect on Windows against a live capture.**
@@ -2087,6 +2118,45 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
2087
2118
  **No file under `ja4plus/` changes and no fingerprint moves**, and the conformance suite
2088
2119
  reports 1635 passed, 143 skipped and 140 xfailed.
2089
2120
 
2121
+ ## [1.3.0] - 2026-09-30
2122
+
2123
+ Version 1.3.0 follows version 1.2.2, and no version stands between them. The date of the
2124
+ heading above is the date this release reaches `master`. It carries rounds 268 and 269.
2125
+
2126
+ **The bump is minor and never patch.** This release adds published names: the
2127
+ `ja4plus.scan` package, the `ja4plus scan` subcommand and the `scan` extra. It removes no
2128
+ name and it renames none.
2129
+
2130
+ **JA4 reads a ClientHello that spans TCP segments** (#772). A post-quantum key share makes
2131
+ a current browser ClientHello longer than one TCP segment, and the fingerprinter read each
2132
+ segment alone, so it wrote no JA4 value for such a hello. **A caller now receives a JA4
2133
+ value for that traffic where it received none before.** The fingerprinter holds the client
2134
+ payload of a connection in a bounded table until the hello is complete, and a gap stops
2135
+ the read at the first byte that has not arrived.
2136
+
2137
+ **Two readers now write the FoxIO value.**
2138
+
2139
+ | Method | Input | Version 1.2.2 | Version 1.3.0 | Issue |
2140
+ |---|---|---|---|---|
2141
+ | JA4 | A ClientHello with a GREASE value among its signature algorithms | Hashes the GREASE value | Removes it, as FoxIO `d66336ef` does | #773 |
2142
+ | JA4T, JA4TS | A SYN or SYN-ACK with a repeated MSS option or window scale option | Keeps the first value | Keeps the last value, as the FoxIO Rust, Wireshark and Zeek sources do | #774 |
2143
+
2144
+ **No value of the committed corpus moves.** A replay of 77 captures writes one new value,
2145
+ the JA4 of `sigalg-grease.pcapng` frame 5, which equals the FoxIO value. Every other value
2146
+ is byte-identical. No committed capture carries a repeated TCP option or a signature
2147
+ algorithm GREASE value.
2148
+
2149
+ **`ja4plus scan` adds JA4TScan, the FoxIO active TCP server fingerprint** (#775, #776). The
2150
+ scanner sends one SYN to each IPv4 target and reads the SYN-ACK and its retransmissions.
2151
+ **It is the one part of the package that sends packets**, so it lives behind the `scan`
2152
+ extra and no passive module imports it. **The library changes no firewall state**, and
2153
+ the command prints the rules the operator adds. `docs/specs/features/12-active-scan.md`
2154
+ states the requirements. The project now implements all twelve FoxIO methods.
2155
+
2156
+ **The FoxIO pin moves from `27f0cbf9` to `16b96d95`**, and `sigalg-grease.pcapng` joins the
2157
+ vectors. The conformance suite reports 1684 passed, 142 skipped and 138 xfailed at version
2158
+ 1.2.2, and 1702 passed, 145 skipped and 137 xfailed here.
2159
+
2090
2160
  ## [1.2.2] - 2026-09-29
2091
2161
 
2092
2162
  Version 1.2.2 follows version 1.2.1, and no version stands between them. The date of the
@@ -4,7 +4,7 @@
4
4
  produces JA4+ fingerprints. FoxIO publishes the JA4+ standard. This project is an
5
5
  independent implementation of it.
6
6
 
7
- This repository declares version 1.2.2, and `ja4plus/__init__.py:101` holds that one
7
+ This repository declares version 1.3.0, and `ja4plus/__init__.py:101` holds that one
8
8
  declaration. A publish to PyPI is a separate step, and the maintainer runs it.
9
9
 
10
10
  Design lives in `docs/specs/`. Read `docs/specs/spec.md` and the relevant
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ja4plus
3
- Version: 1.2.2
3
+ Version: 1.3.0
4
4
  Summary: JA4+ network fingerprinting library for TLS, TCP, HTTP, SSH, X.509, and DHCP analysis
5
5
  License: BSD-3-Clause AND LicenseRef-FoxIO-1.1
6
6
  Project-URL: Homepage, https://github.com/Crank-Git/ja4plus
@@ -41,6 +41,7 @@ Requires-Dist: mkdocs-material==9.7.7; extra == "docs"
41
41
  Requires-Dist: mkdocstrings==1.0.6; extra == "docs"
42
42
  Requires-Dist: mkdocstrings-python==2.0.9; extra == "docs"
43
43
  Requires-Dist: griffe==2.3.0; extra == "docs"
44
+ Provides-Extra: scan
44
45
  Dynamic: license-file
45
46
 
46
47
  <p align="center"><img src="assets/logo.png" width="300"></p>
@@ -49,9 +50,10 @@ A Python library and command-line program that produces JA4+ fingerprints. It re
49
50
  TCP, HTTP, SSH and DHCP traffic, and it reads an X.509 certificate. It supports QUIC,
50
51
  IPv4, IPv6 and multi-segment TCP reassembly.
51
52
 
52
- **FoxIO publishes twelve JA4+ methods, and this project implements eleven of them.** The
53
- twelfth is JA4TScan. `Methods` below names each method, and it states whether this
54
- project builds it.
53
+ **FoxIO publishes twelve JA4+ methods, and this project implements twelve of them.**
54
+ Eleven read traffic that already exists. The twelfth is JA4TScan, which sends packets, and
55
+ the `ja4plus scan` subcommand runs it. Ten fingerprinter classes carry the other eleven,
56
+ because `JA4LFingerprinter` writes both JA4L and JA4LS. `Methods` below names each method.
55
57
 
56
58
  FoxIO owns the JA4+ standard, and [FoxIO](https://foxio.io) publishes it. This library is
57
59
  an independent implementation of that standard. FoxIO wrote no part of it. For the
@@ -80,16 +82,19 @@ The `Implemented` column states whether this project builds the method.
80
82
  | JA4SSH | SSH | Session type classification from traffic patterns | Yes |
81
83
  | JA4T | TCP | Client OS fingerprint from SYN packets | Yes |
82
84
  | JA4TS | TCP | Server fingerprint from SYN-ACK packets | Yes |
83
- | JA4TScan | TCP | Active TCP fingerprint scanner | No |
85
+ | JA4TScan | TCP | Active TCP server fingerprint from SYN-ACK retransmissions, through `ja4plus scan` | Yes |
84
86
  | JA4D | DHCPv4 | DHCP client/server fingerprint (FoxIO PR #267/#270) | Yes |
85
87
  | JA4D6 | DHCPv6 | DHCPv6 client/server fingerprint (FoxIO PR #267/#270) | Yes |
86
88
 
87
- **JA4TScan is declined by ruling, and it is not an omission.** It sends crafted packets
88
- to a host the operator names and reads the responses. Every other method reads traffic
89
- that already exists, so JA4TScan reaches a network the operator did not capture. That
90
- capability is larger than fingerprint production.
91
- [`docs/specs/spec.md`](docs/specs/spec.md) holds the ruling under `Non-goals`, and the
92
- ruling is reversible.
89
+ **JA4TScan sends packets, so this project keeps it apart from the other eleven.** It
90
+ sends one TCP SYN to each host the operator names and reads the responses. The maintainer
91
+ reversed the decline of 2026-08-08 on 2026-09-30, in #775, and #776 built the scanner.
92
+ The scanner installs with `pip install ja4plus[scan]` and runs as `ja4plus scan`. The
93
+ `ja4plus.scan` module holds it, and no passive fingerprinter can send a packet. The
94
+ scanner changes no firewall state, and it states the rule the operator adds.
95
+ [`docs/methods/ja4tscan.md`](docs/methods/ja4tscan.md) describes the command, and
96
+ [`docs/specs/features/12-active-scan.md`](docs/specs/features/12-active-scan.md) holds the
97
+ design.
93
98
 
94
99
  `JA4LFingerprinter` builds both JA4L and JA4LS, so ten fingerprinters carry eleven
95
100
  methods. `--types ja4l` names the two together, and `--types ja4ls` names JA4LS alone.
@@ -535,7 +540,7 @@ pytest tests/ -v
535
540
 
536
541
  This library is released under the **BSD 3-Clause License**.
537
542
 
538
- The JA4+ fingerprinting specifications were created by [FoxIO](https://foxio.io). JA4 (TLS Client) is open source under BSD-3-Clause per FoxIO. The other methods this project implements carry the [FoxIO License 1.1](https://github.com/FoxIO-LLC/ja4/blob/main/LICENSE): JA4S, JA4H, JA4L, JA4LS, JA4X, JA4SSH, JA4T, JA4TS, JA4D and JA4D6. That license is permissive for academic, internal business, and security research use. FoxIO names each of them at line 5 of [`License FAQ.md`](https://github.com/FoxIO-LLC/ja4/blob/main/License%20FAQ.md), at commit `27f0cbf9fd3000c072f82a0f7d0361dc99acf6c8`. That FoxIO list is wider than this one, because it also names methods this project does not implement.
543
+ The JA4+ fingerprinting specifications were created by [FoxIO](https://foxio.io). JA4 (TLS Client) is open source under BSD-3-Clause per FoxIO. The other methods this project implements carry the [FoxIO License 1.1](https://github.com/FoxIO-LLC/ja4/blob/main/LICENSE): JA4S, JA4H, JA4L, JA4LS, JA4X, JA4SSH, JA4T, JA4TS, JA4TScan, JA4D and JA4D6. That license is permissive for academic, internal business, and security research use. FoxIO names each of them at line 5 of [`License FAQ.md`](https://github.com/FoxIO-LLC/ja4/blob/main/License%20FAQ.md), at commit `27f0cbf9fd3000c072f82a0f7d0361dc99acf6c8`. That FoxIO list is wider than this one, because it also names methods this project does not implement.
539
544
 
540
545
  See [LICENSE](LICENSE) for full details.
541
546
 
@@ -4,9 +4,10 @@ A Python library and command-line program that produces JA4+ fingerprints. It re
4
4
  TCP, HTTP, SSH and DHCP traffic, and it reads an X.509 certificate. It supports QUIC,
5
5
  IPv4, IPv6 and multi-segment TCP reassembly.
6
6
 
7
- **FoxIO publishes twelve JA4+ methods, and this project implements eleven of them.** The
8
- twelfth is JA4TScan. `Methods` below names each method, and it states whether this
9
- project builds it.
7
+ **FoxIO publishes twelve JA4+ methods, and this project implements twelve of them.**
8
+ Eleven read traffic that already exists. The twelfth is JA4TScan, which sends packets, and
9
+ the `ja4plus scan` subcommand runs it. Ten fingerprinter classes carry the other eleven,
10
+ because `JA4LFingerprinter` writes both JA4L and JA4LS. `Methods` below names each method.
10
11
 
11
12
  FoxIO owns the JA4+ standard, and [FoxIO](https://foxio.io) publishes it. This library is
12
13
  an independent implementation of that standard. FoxIO wrote no part of it. For the
@@ -35,16 +36,19 @@ The `Implemented` column states whether this project builds the method.
35
36
  | JA4SSH | SSH | Session type classification from traffic patterns | Yes |
36
37
  | JA4T | TCP | Client OS fingerprint from SYN packets | Yes |
37
38
  | JA4TS | TCP | Server fingerprint from SYN-ACK packets | Yes |
38
- | JA4TScan | TCP | Active TCP fingerprint scanner | No |
39
+ | JA4TScan | TCP | Active TCP server fingerprint from SYN-ACK retransmissions, through `ja4plus scan` | Yes |
39
40
  | JA4D | DHCPv4 | DHCP client/server fingerprint (FoxIO PR #267/#270) | Yes |
40
41
  | JA4D6 | DHCPv6 | DHCPv6 client/server fingerprint (FoxIO PR #267/#270) | Yes |
41
42
 
42
- **JA4TScan is declined by ruling, and it is not an omission.** It sends crafted packets
43
- to a host the operator names and reads the responses. Every other method reads traffic
44
- that already exists, so JA4TScan reaches a network the operator did not capture. That
45
- capability is larger than fingerprint production.
46
- [`docs/specs/spec.md`](docs/specs/spec.md) holds the ruling under `Non-goals`, and the
47
- ruling is reversible.
43
+ **JA4TScan sends packets, so this project keeps it apart from the other eleven.** It
44
+ sends one TCP SYN to each host the operator names and reads the responses. The maintainer
45
+ reversed the decline of 2026-08-08 on 2026-09-30, in #775, and #776 built the scanner.
46
+ The scanner installs with `pip install ja4plus[scan]` and runs as `ja4plus scan`. The
47
+ `ja4plus.scan` module holds it, and no passive fingerprinter can send a packet. The
48
+ scanner changes no firewall state, and it states the rule the operator adds.
49
+ [`docs/methods/ja4tscan.md`](docs/methods/ja4tscan.md) describes the command, and
50
+ [`docs/specs/features/12-active-scan.md`](docs/specs/features/12-active-scan.md) holds the
51
+ design.
48
52
 
49
53
  `JA4LFingerprinter` builds both JA4L and JA4LS, so ten fingerprinters carry eleven
50
54
  methods. `--types ja4l` names the two together, and `--types ja4ls` names JA4LS alone.
@@ -490,7 +494,7 @@ pytest tests/ -v
490
494
 
491
495
  This library is released under the **BSD 3-Clause License**.
492
496
 
493
- The JA4+ fingerprinting specifications were created by [FoxIO](https://foxio.io). JA4 (TLS Client) is open source under BSD-3-Clause per FoxIO. The other methods this project implements carry the [FoxIO License 1.1](https://github.com/FoxIO-LLC/ja4/blob/main/LICENSE): JA4S, JA4H, JA4L, JA4LS, JA4X, JA4SSH, JA4T, JA4TS, JA4D and JA4D6. That license is permissive for academic, internal business, and security research use. FoxIO names each of them at line 5 of [`License FAQ.md`](https://github.com/FoxIO-LLC/ja4/blob/main/License%20FAQ.md), at commit `27f0cbf9fd3000c072f82a0f7d0361dc99acf6c8`. That FoxIO list is wider than this one, because it also names methods this project does not implement.
497
+ The JA4+ fingerprinting specifications were created by [FoxIO](https://foxio.io). JA4 (TLS Client) is open source under BSD-3-Clause per FoxIO. The other methods this project implements carry the [FoxIO License 1.1](https://github.com/FoxIO-LLC/ja4/blob/main/LICENSE): JA4S, JA4H, JA4L, JA4LS, JA4X, JA4SSH, JA4T, JA4TS, JA4TScan, JA4D and JA4D6. That license is permissive for academic, internal business, and security research use. FoxIO names each of them at line 5 of [`License FAQ.md`](https://github.com/FoxIO-LLC/ja4/blob/main/License%20FAQ.md), at commit `27f0cbf9fd3000c072f82a0f7d0361dc99acf6c8`. That FoxIO list is wider than this one, because it also names methods this project does not implement.
494
498
 
495
499
  See [LICENSE](LICENSE) for full details.
496
500
 
@@ -280,7 +280,7 @@ dataclass, because a result describes something that already happened.
280
280
 
281
281
  | Field | Type | Constraint |
282
282
  |---|---|---|
283
- | `type` | `str` | The method name, lowercase. One of ten values, and ten values carry eleven methods. `--types` accepts these ten tokens and the token `ja4ls`. |
283
+ | `type` | `str` | The method name, lowercase. One of eleven values. Ten values carry the eleven passive methods, and `ja4plus scan` writes the value `ja4tscan`. `--types` accepts the ten passive tokens and the token `ja4ls`. |
284
284
  | `fingerprint` | `str` | The fingerprint string. Never empty. |
285
285
  | `raw` | `str \| None` | The raw form, when the method defines one. |
286
286
  | `raw_original_order` | `str \| None` | The original-order raw form, when the method defines one. |
@@ -382,8 +382,8 @@ log the error inside the loop that reads it.
382
382
  reads no packet timestamp. The field holds `None`.
383
383
 
384
384
  `stats()` reports what the state tables hold, and #41 built it. One processor holds
385
- **seventeen** state tables across the ten fingerprinters: the fifteen `BoundedStateTable`
386
- instances and the two `TCPStreamReassembler` instances of JA4H and JA4X. A method that
385
+ **eighteen** state tables across the ten fingerprinters: the fifteen `BoundedStateTable`
386
+ instances and the three `TCPStreamReassembler` instances of JA4H, JA4X and JA4 (#772). A method that
387
387
  holds no state reports an empty `tables` list.
388
388
 
389
389
  | Field of `ProcessorStats` | Description |
@@ -435,8 +435,8 @@ one processor without that arrangement, and it guards a `reset` that runs beside
435
435
 
436
436
  #### The memory bound of the processor
437
437
 
438
- One processor holds seventeen state tables: fifteen `BoundedStateTable` instances and
439
- two `TCPStreamReassembler` instances. `features/03-concurrency-safety.md` states the maximum
438
+ One processor holds eighteen state tables: fifteen `BoundedStateTable` instances and
439
+ three `TCPStreamReassembler` instances. `features/03-concurrency-safety.md` states the maximum
440
440
  entry count and the maximum age of each one. A table that reaches its maximum entry count
441
441
  evicts the least recently used entry. A long capture can therefore evict a connection
442
442
  that later returns, and the fingerprint of a returned connection may be incomplete.
@@ -546,11 +546,11 @@ caller removed with `cleanup_connection` counts as a first sighting when it retu
546
546
  because the caller asked for that removal.
547
547
 
548
548
  A table remembers the keys it evicted, so that it can recognize a return. The memory
549
- holds the entry bound of its own table. The seventeen tables of one processor hold 57400
550
- remembered keys between them, at 187 bytes for one key, so the memory costs 10.2 MiB
549
+ holds the entry bound of its own table. The eighteen tables of one processor hold 58400
550
+ remembered keys between them, at 187 bytes for one key, so the memory costs 10.4 MiB
551
551
  when every table is full and every entry of every table has been replaced.
552
552
 
553
- Ten fingerprinters hold seventeen state tables between them. `JA4DFingerprinter` and
553
+ Ten fingerprinters hold eighteen state tables between them. `JA4DFingerprinter` and
554
554
  `JA4D6Fingerprinter` hold none, and each reports an empty `tables` dict.
555
555
 
556
556
  `stats()` holds the lock of one fingerprinter across the read of that fingerprinter, so
@@ -562,6 +562,61 @@ tables that the counts describe.
562
562
 
563
563
  `ProcessorStats` is a plain object. Epic 4 makes it a typed dataclass.
564
564
 
565
+ ## Active scan
566
+
567
+ `ja4plus.scan` holds JA4TScan, the one method that sends packets. `import ja4plus` loads
568
+ no module of it, and no module outside it imports it. The `ja4plus scan` subcommand
569
+ reaches it through the `ja4plus.commands` entry point.
570
+ [The JA4TScan page](methods/ja4tscan.md) describes the command.
571
+
572
+ **The scan loop takes the send function, the receive function and the clock as
573
+ parameters.** A caller therefore runs it on a fake network, and `tests/scan_fakes.py`
574
+ holds one.
575
+
576
+ ### ja4plus.scan.value
577
+
578
+ | Class/Function | Description |
579
+ |---|---|
580
+ | `Response(seconds, flags, window, options)` | The four fields of one TCP response that the value reads |
581
+ | `ja4tscan_value(responses)` | Return the JA4TScan value of one target, or `None` when it sent no response |
582
+ | `RST_ACK_VALUE` | The value `0_rst-ack`, which a target whose first response carries RST produces |
583
+
584
+ ### ja4plus.scan.frames
585
+
586
+ | Class/Function | Description |
587
+ |---|---|
588
+ | `build_syn(*, src_mac, dst_mac, src_ip, dst_ip, src_port, dst_port, sequence, timestamp)` | Return the Ethernet frame of one FoxIO SYN, 74 bytes |
589
+ | `parse_frame(frame)` | Return the TCP fields of one received Ethernet frame, or `None` for a frame it cannot read |
590
+ | `Reply` | The TCP fields of one received segment that the scanner reads |
591
+
592
+ ### ja4plus.scan.scanner
593
+
594
+ | Class/Function | Description |
595
+ |---|---|
596
+ | `Scanner(*, port, rate, retransmit, send, receive, clock, on_result, on_warning, rng)` | The scan loop. `run(targets)` sends one SYN to each target and waits for its responses |
597
+ | `ScanResult` | The value of one target and the endpoints of its responses |
598
+ | `parse_targets(text)` | Return the IPv4 targets that one address, one network or one file names |
599
+ | `TargetError` | The error `parse_targets` raises for a target the scanner cannot read |
600
+ | `firewall_rules(platform)` | Return the firewall rules the operator adds before a scan |
601
+ | `MAX_TARGETS` | The largest target count the state table holds, 10000 |
602
+ | `RETRANSMIT_WAIT_SECONDS` | The wait for later responses after each SYN, 120 seconds |
603
+ | `NO_RETRANSMIT_WAIT_SECONDS` | The wait under `--retransmit no`, 8 seconds |
604
+
605
+ ### ja4plus.scan.command
606
+
607
+ | Class/Function | Description |
608
+ |---|---|
609
+ | `run(args, result_stream)` | Run the scan on the network of this host. The `ja4plus.commands` entry point names it |
610
+ | `scan_command(args, *, result_stream, platform, open_network, clock, rng)` | Run the scan over the network that `open_network` returns |
611
+ | `METHOD_TYPE` | The `type` value of a scan output line, `ja4tscan` |
612
+
613
+ ### ja4plus.scan.link
614
+
615
+ | Class/Function | Description |
616
+ |---|---|
617
+ | `LinkNetwork(port, first_target, on_warning)` | The link-layer socket that sends each SYN and reads each response. It needs the privilege to open a raw socket |
618
+ | `privilege_refused(error)` | Return `True` when the error states that the host refused that privilege |
619
+
565
620
  ## Utility Modules
566
621
 
567
622
  ### ja4plus.utils.tls_utils
@@ -300,17 +300,23 @@ reported green on a comparison it never made. #216 closed it, and
300
300
  | Measurement | Count |
301
301
  |---|---|
302
302
  | JA4T values in `python/test/testdata/` | 0 |
303
- | JA4T values in the seven local Rust snapshots that hold one | 39 |
304
- | Values `ja4plus` reproduces exactly | 38 |
305
- | Values that differ | 1 |
303
+ | JA4T values in the eight local Rust snapshots that hold one | 40 |
304
+ | Values `ja4plus` reproduces exactly | 40 |
305
+ | Values that differ | 0 |
306
306
  | Streams on which `ja4plus` emits more than one value | 0 |
307
- | Cases that stop running when `("JA4T", "ja4t")` leaves `SNAPSHOT_METHODS` | 46 |
307
+ | Cases that stop running when `("JA4T", "ja4t")` leaves `SNAPSHOT_METHODS` | 44 |
308
+
309
+ **#772 took the four counts above again on 2026-09-30, after it moved the pin of
310
+ `tests/foxio_vectors/` to FoxIO `16b96d95`.** `sigalg-grease.pcapng` adds one snapshot and
311
+ one value. The last count is the drop in the collected cases of
312
+ `tests/test_foxio_rust_parity.py` when the JA4T entry leaves `SNAPSHOT_METHODS`.
308
313
 
309
- **The one entry.** `tests/foxio_deviations.json` holds one JA4T key today, and it is
310
- `gre-erspan-vxlan.pcap/0:65174/JA4T.1`. The FoxIO Rust snapshot holds `8192__0_0` and
311
- `ja4plus` writes `8192_00_00_00`. The SYN carries no TCP option, and the three FoxIO forms
312
- disagree. The user chose the two-digit form on 2026-08-08, so the entry is `decided` and it
313
- stays. #215 records the ruling.
314
+ **The register holds no JA4T key.** It held one until #772, which was
315
+ `gre-erspan-vxlan.pcap/0:65174/JA4T.1`. The FoxIO Rust snapshot held `8192__0_0` at the
316
+ earlier pin, and `ja4plus` writes `8192_00_00_00`. The user chose the two-digit form on
317
+ 2026-08-08, and #215 records the ruling. FoxIO `08617cc3` moved the Rust implementation to
318
+ the same form, so the snapshot at `16b96d95` holds `8192_00_00_00` and #772 removed the
319
+ entry.
314
320
 
315
321
  **Two entries this section named have gone, and the repairs removed them.**
316
322
  `chrome-cloudflare-quic-with-secrets.pcapng/0:57098/JA4T.1` recorded a part b that held one
@@ -540,7 +546,7 @@ value. #132 holds the command and the full output.
540
546
  Before #132, `ja4plus` emitted `9af15b336e6a` on these four streams. `JA4_o` now matches
541
547
  all 160 reference values, and the count was 156 of 160.
542
548
 
543
- **Location:** `ja4plus/fingerprinters/ja4.py:226`.
549
+ **Location:** `ja4plus/fingerprinters/ja4.py:228`.
544
550
 
545
551
  The JA4S section below records the `JA4S_o` reading.
546
552
 
@@ -1,6 +1,8 @@
1
1
  # The methods
2
2
 
3
- FoxIO publishes twelve JA4+ methods. This project implements eleven of them.
3
+ FoxIO publishes twelve JA4+ methods. This project implements twelve of them. Ten
4
+ fingerprinter classes carry eleven of them, because `JA4LFingerprinter` writes both JA4L
5
+ and JA4LS. The `ja4plus scan` subcommand carries JA4TScan.
4
6
 
5
7
  Each page below describes one method. It states the output format, the parts of the
6
8
  value, the hash rule, and the FoxIO file that publishes the method. It also names one
@@ -23,7 +25,7 @@ the standard itself, read the
23
25
  | JA4SSH | SSH | [The SSH session fingerprint](ja4ssh.md) | Yes |
24
26
  | JA4T | TCP | [The TCP client fingerprint](ja4t.md) | Yes |
25
27
  | JA4TS | TCP | [The TCP server fingerprint](ja4ts.md) | Yes |
26
- | JA4TScan | TCP | No page. Read the decline below. | No |
28
+ | JA4TScan | TCP | [The active TCP server fingerprint](ja4tscan.md) | Yes |
27
29
  | JA4D | DHCPv4 | [The DHCPv4 fingerprint](ja4d.md) | Yes |
28
30
  | JA4D6 | DHCPv6 | [The DHCPv6 fingerprint](ja4d6.md) | Yes |
29
31
 
@@ -40,15 +42,24 @@ under parity rule 2.
40
42
  **Read the ten as a count of fingerprinter classes, and never as a count of methods.**
41
43
  #387 records the three documents that made that mistake.
42
44
 
43
- ## Why this project builds no JA4TScan
45
+ ## Why JA4TScan stands apart from the other methods
44
46
 
45
- **This project declines JA4TScan by ruling, and the absence is no omission.** JA4TScan
46
- sends crafted packets to a host the operator names, and it reads the responses. Every
47
- other method reads traffic that already exists, so JA4TScan reaches a network the
48
- operator did not capture. That capability is larger than fingerprint production.
47
+ **JA4TScan sends packets, and every other method reads traffic that already exists.**
48
+ JA4TScan sends one TCP SYN to each host the operator names, and it reads the responses.
49
+ #776 built it as the `ja4plus scan` subcommand, in the `ja4plus.scan` module. No
50
+ fingerprinter of this table can send a packet.
49
51
 
50
- `docs/specs/spec.md` holds the ruling under `Non-goals`, and #197 holds the reading.
51
- The ruling is reversible.
52
+ The maintainer reversed the decline of 2026-08-08 on 2026-09-30, in #775.
53
+ `docs/specs/features/12-active-scan.md` holds the design, and [the JA4TScan page](ja4tscan.md)
54
+ describes the command. The superseded text is quoted below rather than rewritten.
55
+
56
+ > **This project declines JA4TScan by ruling, and the absence is no omission.** JA4TScan
57
+ > sends crafted packets to a host the operator names, and it reads the responses. Every
58
+ > other method reads traffic that already exists, so JA4TScan reaches a network the
59
+ > operator did not capture. That capability is larger than fingerprint production.
60
+ >
61
+ > `docs/specs/spec.md` holds the ruling under `Non-goals`, and #197 holds the reading.
62
+ > The ruling is reversible.
52
63
 
53
64
  ## What a fingerprint is evidence of
54
65
 
@@ -21,8 +21,8 @@ fingerprint.
21
21
  <part a>_<cipher hash>_<extension hash>
22
22
  ```
23
23
 
24
- Part a holds ten characters. `ja4plus/fingerprinters/ja4.py:316` builds it, and
25
- `ja4plus/fingerprinters/ja4.py:231` joins the three parts.
24
+ Part a holds ten characters. `ja4plus/fingerprinters/ja4.py:318` builds it, and
25
+ `ja4plus/fingerprinters/ja4.py:233` joins the three parts.
26
26
 
27
27
  ## The parts
28
28
 
@@ -35,7 +35,7 @@ Part a holds ten characters. `ja4plus/fingerprinters/ja4.py:316` builds it, and
35
35
  | Extension count | 2 | The count of extensions, after the reader drops every GREASE value. It stops at `99`. |
36
36
  | ALPN | 2 | The first character and the last character of the first ALPN value, or `00` when the client offers none. |
37
37
  | Cipher hash | 12 | The hash of the sorted cipher list. |
38
- | Extension hash | 12 | The hash of the sorted extension list, with the signature algorithms appended. |
38
+ | Extension hash | 12 | The hash of the sorted extension list, with the signature algorithms appended. The reader drops every GREASE value from the signature algorithms and keeps their wire order. |
39
39
 
40
40
  ## The hash rule
41
41
 
@@ -43,9 +43,13 @@ writes `00`, the MSS part writes two digits, and a window scale of zero writes `
43
43
  window scale above zero writes its own digits and no padding, which is why the example
44
44
  below ends in `7` and not `07`.
45
45
 
46
- The Wireshark dissector and the Zeek script both write that form, and the FoxIO Rust
47
- implementation writes one digit. #215 records the ruling as D1, and the
48
- `Divergence register` of `docs/specs/spec.md` records the cost.
46
+ The Wireshark dissector, the Zeek script and the FoxIO Rust implementation each write that
47
+ form. The Rust implementation wrote one digit until FoxIO `08617cc3`. #215 records the
48
+ ruling as D1, and the `Divergence register` of `docs/specs/spec.md` records the reading.
49
+
50
+ **A repeated option keeps its last value.** A SYN that carries two MSS options writes the
51
+ value of the second one, and a repeated window scale option works the same way. The three
52
+ FoxIO implementations each keep the last value. #774 records the reading.
49
53
 
50
54
  ## The hash rule
51
55