Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ Version numbers follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html

---

## [0.4.1] — 2026-08-18

### Removed

- The unverified-TLS warning banner from the terminal report and file
exports. Unverified probing is the default mode since 0.4.0, so the banner
fired on every HTTPS scan and carried no signal. `tls_verified` remains
available in the `--json` output and on `HeadersReport` for machine
consumers.

---

## [0.4.0] — 2026-08-18

### Added
Expand Down Expand Up @@ -343,7 +355,8 @@ Version numbers follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html

---

[Unreleased]: https://github.com/NC3-TestingPlatform/headersvalidator/compare/v0.4.0...HEAD
[Unreleased]: https://github.com/NC3-TestingPlatform/headersvalidator/compare/v0.4.1...HEAD
[0.4.1]: https://github.com/NC3-TestingPlatform/headersvalidator/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/NC3-TestingPlatform/headersvalidator/compare/v0.3.2...v0.4.0
[0.3.2]: https://github.com/NC3-TestingPlatform/headersvalidator/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/NC3-TestingPlatform/headersvalidator/compare/v0.3.0...v0.3.1
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ $ headersvalidator check example.com
```

![Python](https://img.shields.io/badge/python-%3E%3D3.11-blue)
![Tests](https://img.shields.io/badge/tests-435%20passing-brightgreen)
![Tests](https://img.shields.io/badge/tests-433%20passing-brightgreen)
![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
![License](https://img.shields.io/badge/license-GPLv3-lightgrey)

Expand Down Expand Up @@ -308,7 +308,7 @@ pytest tests/test_checker.py
pytest tests/test_checker.py::TestHSTS -v
```

The test suite has **435 tests** and maintains **100% statement coverage**.
The test suite has **433 tests** and maintains **100% statement coverage**.

All HTTP network I/O (`requests.head`, `requests.get`) is mocked at the
`fetch_headers` boundary — no test touches a real server or the internet.
Expand Down
4 changes: 2 additions & 2 deletions docs/SECURITY_VERDICT.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ Total penalty is mapped to a letter grade:
> verify certificates by default (no request is spent on a verification
> attempt and self-signed or invalid certificates cannot block the
> assessment; the pre-existing HTTP fallback on a refused HTTPS connection
> is unchanged); the report is flagged
> instead (`tls_verified: false`, plus a warning banner). Certificate posture
> is unchanged); the report carries `tls_verified: false` in the `--json`
> output and on `HeadersReport` instead. Certificate posture
> is assessed and graded by **tlsvalidator**, not here — this module grades
> only the headers themselves, exactly as it already does for targets served
> over plain HTTP.
Expand Down
2 changes: 1 addition & 1 deletion headersvalidator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
try:
__version__ = version("headersvalidator")
except PackageNotFoundError: # pragma: no cover – only when package not installed
__version__ = "0.4.0"
__version__ = "0.4.1"

# NullHandler so library users who have not configured logging
# do not see "No handler found" warnings (PEP 3118 / logging HOWTO).
Expand Down
13 changes: 0 additions & 13 deletions headersvalidator/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,6 @@
VerdictSeverity.INFO: "dim",
}

# Plain-text body of the unverified-TLS warning banner; also used to size
# file exports so the banner is never wrapped.
_TLS_UNVERIFIED_NOTICE = (
"⚠ TLS certificate verification disabled — "
"headers were fetched over an unverified HTTPS connection."
)

# Grade letter → Rich colour string
_GRADE_STYLE: dict[str, str] = {
"A+": "bold bright_green",
Expand Down Expand Up @@ -87,11 +80,6 @@ def print_full_report(report: HeadersReport, console: Console | None = None) ->
f" [dim]→ redirected to[/dim] [cyan]{report.final_url}[/cyan]",
highlight=False,
)
if not report.tls_verified:
con.print(
f" [bold yellow]{_TLS_UNVERIFIED_NOTICE}[/bold yellow]",
highlight=False,
)
con.print()
_print_results_table(report, con)
actions = extract_verdict_actions(report)
Expand Down Expand Up @@ -299,7 +287,6 @@ def save_report(path: str, report: HeadersReport) -> None:
header_floor = max(
len(f"HTTP Headers Report — {report.url}"),
len(f" → redirected to {report.final_url}"),
len(f" {_TLS_UNVERIFIED_NOTICE}") if not report.tls_verified else 0,
) + 4
width = max(content_width, header_floor)
file_console = Console(record=True, highlight=False, width=width, file=StringIO())
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "headersvalidator"
version = "0.4.0"
version = "0.4.1"
description = "HTTP response header validator — RFC 9110, RFC 9111, OWASP, IANA"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
21 changes: 3 additions & 18 deletions tests/test_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,12 @@ def test_no_redirect_line_when_final_url_matches(self):
output = _capture(print_full_report, report)
assert "redirected to" not in output

def test_shows_banner_when_tls_unverified(self):
def test_no_tls_banner_in_output(self):
"""tls_verified is exposed via --json / the API only — the terminal
report carries no per-scan banner (unverified is the default mode)."""
report = _make_report(("X-Frame-Options", Status.PASS))
report.tls_verified = False
output = _capture(print_full_report, report)
assert "unverified HTTPS connection" in output

def test_no_banner_when_tls_verified(self):
report = _make_report(("X-Frame-Options", Status.PASS))
output = _capture(print_full_report, report)
assert "unverified" not in output


Expand Down Expand Up @@ -317,18 +314,6 @@ def test_saves_svg(self, tmp_path):
assert "X-Frame-Options" in content
assert "<svg" in content.lower()

def test_saved_txt_contains_unwrapped_tls_banner(self, tmp_path):
report = _make_report(("X-Frame-Options", Status.PASS))
report.tls_verified = False
path = str(tmp_path / "report.txt")
save_report(path, report)
content = (tmp_path / "report.txt").read_text()
# The full banner must survive export on a single line (the export
# width floor accounts for it).
assert (
"headers were fetched over an unverified HTTPS connection." in content
)

def test_raises_on_unknown_extension(self, tmp_path):
import pytest

Expand Down