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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,14 @@ field out.
prints `TLS 1.0 ! Supported (Deprecated)` and
`legend: ✓ met, ◐ partial, ○ in progress, ✗ not met, - not yet required`.
The words and colours are unchanged.
- **The README's quick start starts with the release binary.** It opened with
`git clone`, `go build` and `sudo mv`, which need Go 1.25+ and `sudo`, and
left the pre-built archives to a link. The first block now downloads the
latest release's archive for macOS on Apple silicon and its `checksums.txt`
with `curl`, unpacks the archive only after `shasum` reports it `OK`, and runs
`./tlsanalyzer version`. Building from source comes second and runs the
binary where it was built, without `sudo`. `docs/install.md` names the
archive for each platform.

## [0.4.1] - 2026-08-05

Expand Down
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ Releases are managed by maintainers:
3. Create annotated tag: `git tag -a v0.x.0 -m "Release v0.x.0"`
4. Push tag: `git push origin v0.x.0`
5. GitHub Actions builds and publishes release
6. Render the Homebrew formula from the release's published `checksums.txt`, and
commit the output to [csnp/homebrew-tap](https://github.com/csnp/homebrew-tap)
as `Formula/tlsanalyzer.rb`:

```bash
dir=$(mktemp -d)
curl -fsSL -o "$dir/checksums.txt" https://github.com/csnp/tls-analyzer/releases/download/v0.x.0/checksums.txt
go run ./packaging/brewformula -version 0.x.0 -checksums "$dir/checksums.txt" > "$dir/tlsanalyzer.rb"
```

## Getting Help

Expand Down
35 changes: 24 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,44 @@ Part of the [QRAMM (Quantum Readiness Assurance Maturity Model)](https://qramm.o

## Quick Start

### Option 1: Build from Source
### Option 1: Download a binary

Requires Go 1.25+ ([install Go](https://go.dev/doc/install)), which is what `go.mod` declares
This needs no Go toolchain and no `sudo`. On macOS with Apple silicon, copy and
paste this block. It downloads the latest release's archive and its
`checksums.txt`, checks the archive against that file, and unpacks the
`tlsanalyzer` binary into the current directory:

```bash
curl -fsSLO https://github.com/csnp/tls-analyzer/releases/latest/download/tlsanalyzer-darwin-arm64.tar.gz
curl -fsSLO https://github.com/csnp/tls-analyzer/releases/latest/download/checksums.txt
shasum -a 256 -c --ignore-missing checksums.txt && tar -xzf tlsanalyzer-darwin-arm64.tar.gz
./tlsanalyzer version
```

`tar` runs only when the check prints `tlsanalyzer-darwin-arm64.tar.gz: OK`. For
an Intel Mac, Linux or Windows, [docs/install.md](docs/install.md) names the
archive to download.

### Option 2: Build from source

Requires Go 1.25+ ([install Go](https://go.dev/doc/install)), which is what `go.mod` declares.

Copy and paste this entire block:

```bash
git clone https://github.com/csnp/qramm-tls-analyzer.git
cd qramm-tls-analyzer
go build -o tlsanalyzer ./cmd/tlsanalyzer
sudo mv tlsanalyzer /usr/local/bin/
cd ..
tlsanalyzer version
./tlsanalyzer version
```

### Option 2: Download Binary

Download pre-built binaries from [Releases](https://github.com/csnp/qramm-tls-analyzer/releases).
To check a download against the release's `checksums.txt`, follow [docs/install.md](docs/install.md).
### Run your first scan

### Run Your First Scan
From the directory that holds `tlsanalyzer`:

```bash
# Scan a domain you own or have permission to test
tlsanalyzer yourdomain.com
./tlsanalyzer yourdomain.com
```

Expected output: Security grade, quantum risk score, CNSA 2.0 timeline.
Expand Down
116 changes: 116 additions & 0 deletions cmd/tlsanalyzer/readme_quick_start_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package main

import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)

// TestReadmeQuickStartLeadsWithTheReleaseBinary keeps the README's first way
// to install the tool one that needs no Go toolchain and no sudo.
//
// The quick start opened with git clone, go build and sudo mv, so a first scan
// needed Go 1.25+ and sudo, while each release from 0.4.0 publishes archives
// for macOS, Linux and Windows and a checksums.txt to check them against. The
// first bash block of the quick start has to download a release archive and
// checksums.txt by names the release workflow publishes, and no command in the
// quick start may run sudo.
func TestReadmeQuickStartLeadsWithTheReleaseBinary(t *testing.T) {
path := filepath.Join("..", "..", "README.md")
body, err := os.ReadFile(path)
if err != nil {
t.Fatalf("reading %s: %v", path, err)
}

type command struct {
line int // line number in README.md
block int // 0 for the quick start's first bash block
text string
}
var commands []command
inQuickStart, inBash, block := false, false, -1
for i, line := range strings.Split(string(body), "\n") {
if line == "## Quick Start" {
inQuickStart = true
continue
}
if !inQuickStart {
continue
}
if strings.HasPrefix(line, "## ") {
break
}
fence := strings.TrimSpace(line)
switch {
case inBash && fence == "```":
inBash = false
case inBash:
commands = append(commands, command{i + 1, block, strings.TrimSpace(line)})
case fence == "```bash":
inBash, block = true, block+1
}
}
if len(commands) == 0 {
t.Fatalf("%s has no bash block under \"## Quick Start\"", path)
}

published := releaseAssets(t)
download := regexp.MustCompile(`releases/latest/download/(\S+)`)
var downloads []string
for _, c := range commands {
if strings.HasPrefix(c.text, "sudo ") {
t.Errorf("%s:%d: the quick start runs sudo: %s", path, c.line, c.text)
}
if c.block != 0 {
continue
}
for _, tool := range []string{"git clone", "go build", "go install"} {
if strings.Contains(c.text, tool) {
t.Errorf("%s:%d: the quick start's first bash block runs %s, a step of building from source; "+
"lead with the release binary, which needs no Go toolchain: %s", path, c.line, tool, c.text)
}
}
for _, m := range download.FindAllStringSubmatch(c.text, -1) {
downloads = append(downloads, m[1])
if !published[m[1]] {
t.Errorf("%s:%d: downloads %s, which the release workflow does not publish", path, c.line, m[1])
}
}
}
archive := false
checksums := false
for _, name := range downloads {
archive = archive || strings.HasPrefix(name, "tlsanalyzer-")
checksums = checksums || name == "checksums.txt"
}
if !archive || !checksums {
t.Errorf("%s: the quick start's first bash block downloads %q from the latest release; "+
"want a release archive and checksums.txt", path, downloads)
}
}

// releaseAssets returns the names .github/workflows/release.yml publishes: an
// archive per matrix suffix, a zip for Windows and a tar.gz for the others, as
// its "Create archive" step builds them, and checksums.txt.
func releaseAssets(t *testing.T) map[string]bool {
t.Helper()
path := filepath.Join("..", "..", ".github", "workflows", "release.yml")
body, err := os.ReadFile(path)
if err != nil {
t.Fatalf("reading %s: %v", path, err)
}
assets := map[string]bool{"checksums.txt": true}
for _, m := range regexp.MustCompile(`(?m)^\s*suffix:\s*(\S+)\s*$`).FindAllStringSubmatch(string(body), -1) {
if strings.HasPrefix(m[1], "windows-") {
assets["tlsanalyzer-"+m[1]+".zip"] = true
} else {
assets["tlsanalyzer-"+m[1]+".tar.gz"] = true
}
}
if len(assets) == 1 {
t.Fatalf("%s has no matrix suffix lines to name its archives by", path)
}
return assets
}
20 changes: 17 additions & 3 deletions docs/install.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
# Installing QRAMM TLS Analyzer

The README's quick start builds the tool from source. This page covers the
other way in: a pre-built binary, and how to check it before you run it.
The README's quick start downloads the pre-built binary for macOS on Apple
silicon and checks it before unpacking it. This page names the archive for each
platform and explains the check.

## Download Binary

Download pre-built binaries from [Releases](https://github.com/csnp/qramm-tls-analyzer/releases).
Every release from 0.4.0 publishes one archive per platform:

| Platform | Archive |
|---|---|
| macOS on Apple silicon | `tlsanalyzer-darwin-arm64.tar.gz` |
| macOS on Intel | `tlsanalyzer-darwin-amd64.tar.gz` |
| Linux on x86-64 | `tlsanalyzer-linux-amd64.tar.gz` |
| Linux on ARM64 | `tlsanalyzer-linux-arm64.tar.gz` |
| Windows on x86-64 | `tlsanalyzer-windows-amd64.zip` |

The latest release's files download from
`https://github.com/csnp/tls-analyzer/releases/latest/download/` followed by the
file name, which is how the README's block fetches them. Every release is also
listed on [Releases](https://github.com/csnp/qramm-tls-analyzer/releases).

Every release from 0.4.0 also publishes `checksums.txt`. Download it into the same
directory as the archive and verify before extracting:
Expand Down
Loading
Loading