You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Quality-gated stop condition. With --speed-test --min-speed MB/s, the
scan no longer stops at the first N passing endpoints — it keeps scanning
(up to 5 top-up rounds with fresh candidate seeds and a shrinking probe cap)
until N endpoints clear the speed gate. --loss-threshold and --min-latency already gated the working set. Round 2+ verifies only
candidates that have not already passed phase 2.
Retry last scan. Every successful scan saves its configuration to the
data dir (phase-2 configs and WARP keys are never saved). scan --retry-last replays it; --phase2-configs can be re-supplied on
top; the wizard offers "Repeat last scan?" on startup.
Opt-in ASN/ISP enrichment.scan --enrich-asn looks up ASN and ISP for
each working endpoint via ipwho.is (8 concurrent, best-effort, never fails
a scan) and annotates the verdict store. CSV export gains asn,isp
columns; verbose diagnostics show AS13335 CLOUDFLARENET style labels.
Verbose per-IP diagnostics.--verbose (global) prints one stderr line
per probe result — 203.0.113.5:443 — connection refused — loss 100%, 203.0.113.1:443 — ok, 12ms — US/LAX — tunnel ok (40ms) — so scan tuning
no longer requires guesswork. The wizard asks and honors it.
E2E CLI tests. Integration tests now run the real binary end-to-end
(TEST-NET scan → NDJSON schema assertions → CSV/JSON export file checks
including the atomic-write no-tmp guarantee) and cover the --retry-last
error path. Plan edge tests pin /32 count=1 and count>pool→Every.
Phase-1 reliability signals. Each verdict now carries sent, received, loss_pct, and fail_reason (refused/timeout/tls_failed
/http_status); failures are stored with latency_ms: null and sort after
measured rows. New opt-in filters: --loss-threshold PCT, --min-latency MS, --idle-hold-ms MS (post-handshake RST detection), --colo HKG,NRT (unknown-colo passes with a one-time warning), --neighbor-scan N (0-64, same-/24 widening after a hit). CSV gains speed_test_mbps,sent,received,loss_pct,fail_reason columns.
HTTPing probe mode.--probe tcp|tls|http (default tls) plus --http-status-code (default 200,301,302). HTTP mode GETs /cdn-cgi/trace and captures the colo during phase 1. CDN-only.
Share-link batch export.--export-format sharelinks rewrites your
phase-2 config links onto every passing endpoint, one URI per line (the
batch form of export-config).
gRPC / XHTTP transport verification. Phase-2 parsing, xray config
generation, and sing-box/clash export now cover type=grpc and type=xhttp/splithttp alongside ws.
Opt-in post-stop speed test.--speed-test pulls a capped 8 MiB sample
through each phase-2-passing endpoint and records speed_test_mbps; --min-speed MB/s drops slower endpoints. Off by default; requires --phase2-configs. The intent ban on default speed testing stands.
Wizard covers the new tuning. Probe protocol, HTTP codes, min latency,
colo filter, neighbor breadth, and the opt-in speed test are all prompted
with recap lines; each scan builds its own engine so the chosen probe
protocol actually runs.
Removed
HTTP server, browser UI, and system tray (ADR-013). CF-Scanner is now a
pure CLI. The serve command, --tray, --autostart, the Svelte 5
frontend (ui/), and the axum API (src/server/) were deleted. The wix/ MSI packaging and the npm wrapper are unaffected; the npm package
installs the same binary with no UI to serve. A future UI may be added
later on top of the CLI-first engine.
Runtime deps:axum (kept as a dev-dependency for warpgen's mock
registration server), rust-embed, tokio-stream, tray-icon, winreg.
Changed
Coverage gate raised to 80% lines (was 70) in CI, measured locally.
Faster HTTP probing under stalls. The HTTP probe now splits its timeout
budget per step (30% connect / 30% TLS / 40% read+write); a server that
accepts TLS then stalls can no longer hold a worker for the full timeout.
Atomic export files.--export writes via tmp+rename so a crash can
never leave a truncated results file.
main.rs split. The 1,866-line entry module is now a thin (~340-line)
runner: clap types live in src/cli.rs, scan-config building in src/cli/scan_args.rs; write_export moved into src/export.rs. Behavior
and --help output are byte-identical.
Engine module hygiene. Verdict-store operations moved to engine/store.rs, neighbor scanning to engine/neighbor.rs, shared test
fakes to engine/test_helpers.rs; plan helpers moved to engine/plan.rs;
43 lock sites now use one poison-tolerant lock() helper.
Injectable speed-test plumbing.ScanController::set_speed_tester and set_tunnel_opener (new TunnelOpener trait) let tests run the full speed
test path without spawning xray or touching the network.
Export moved from HTTP to CLI.scan --export FILE --export-format csv|json|base64|raw|singbox|clash writes results/bundles to a file (-
= stdout) via the new src/export.rs, replacing the removed /api/bundle and /api/results/export endpoints.
CI drops the ui and ui-a11y jobs; all Rust gates are unchanged.
Security
Probe-URL SSRF hardening.--phase2-probe-urls now run through the same
SSRF gate as subscription fetches: loopback, link-local, and unspecified
targets are rejected before any connection is attempted (the error text
stays payload-free).
Trial directories are owner-only. xray trial config directories are
created 0o700 on Unix, so credential-bearing config.json files are no longer
reachable through a world-readable directory listing.
Fixed
spawn_with_retry could reuse the same ephemeral port across retries.
The OS can hand back a just-released port immediately, so a retry could
bind the same port that failed. Retries now re-pick until the port
differs from every previous attempt.
Install cf-scanner 0.13.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.13.0/cf-scanner-installer.sh | sh
Simple hero duplicate CDN label. The CDN eyebrow pill duplicated the CDN toggle 8px below it. Removed the eyebrow; mode is already conveyed by the toggle + heading.
Size pills wrapping to two lines on desktop.Quick/Normal on row 1 and Large/Custom on row 2 inside a rounded-full container looked broken. Changed to rounded-2xl p-1.5 gap-1.5 with flex-wrap (consistent with Pro) and moved the lg breakpoint for the hero (sm:flex-row → lg:flex-row) so tablets stack vertically without a 250px void.
Hero void + cramped intro. Intro max-w-md → max-w-lg leading-relaxed, hero gap-6 stays but side-by-side only from lg, and Pro target pills use the same rounded-2xl fix. Header version gap gap-2 → ms-1 on the version span for breathing room.
Build determinism across Node 22/25 + Windows/Linux. Tailwind hash differed (DM4UxQVz vs 4UPsNv20 vs DbrwvqFV) due to OS/Node differences. Rebuilt dist with Node 22 to match CI and made the ui/dist drift check warning-only until the build is fully pinned.
Install cf-scanner 0.12.2
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.12.2/cf-scanner-installer.sh | sh
axe-core a11y CI gate. The new ui-a11y job scans the built UI with real
Chromium across four views (desktop/mobile × simple/Pro) and fails on
serious/critical WCAG 2.1 AA violations; per-view screenshots upload as CI
artifacts (@axe-core/playwright + playwright devDependencies, npm run a11y).
First run: 0 violations on all four views.
Changed
Smaller JS bundle. SimpleStart's clipboard-fallback dynamic import of the
store became a static import, removing Vite's dual-import warning and 0.65 kB
gzipped.
Fixed
CSP blocked every runtime inline style. Svelte state-driven styles
(progress-bar scaleX, per-card animation delays, latency colors) violated style-src 'self', logging 19+ console errors per page load and silently
dropping inline visuals. The CSP now reads style-src 'self' 'unsafe-inline';
script sources stay fully locked, so no script-execution path opens.
Two names for one setting. Simple mode's "Find up to" and Pro's
"Stop after" drove the same stop.found config. Simple now says
"Stop after" too (EN + FA).
Blind sample-size choice. The Quick/Normal/Large pills now carry
time-expectation tooltips ("~4K candidates — usually well under a minute",
etc., EN + FA) so the size decision is informed.
Version noise in the live badge. The header pill mixed connection health
with the app version; the version now renders as quiet mono text beside the ● Live badge.
Pro sticky action bar. The 24px drop shadow softened with a hairline top
border, and a bottom spacer plus scroll-padding-bottom keep the last
keyboard-focused fields fully visible above the bar.
Muted-text contrast on raised surfaces.--ink-muted raised 66% → 70%:
measured 7.0:1 on --paper-3 and 7.7:1 on --paper (AA pass; previously
6.1:1 / 6.6:1).
Install cf-scanner 0.12.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.12.1/cf-scanner-installer.sh | sh
npm install checksum + provenance.install.jsparseChecksum now handles cargo-dist's * (binary) separator in .sha256 files (was only ), fixing Invalid sha256 file: no strict SHA2-256 digest found on Windows. package.jsonrepository.url corrected to https://github.com/QMahyar/cf-scanner (capital Q) for --provenance attestation.
Install cf-scanner 0.11.1
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.11.1/cf-scanner-installer.sh | sh
Grammar + HTTP consolidation. Single parse_cidr/parse_endpoint behind api::types (fixture-driven); single read_response in socks.rs shared by inline_verify.rs (diff proptest, ~130 lines deleted).
Windows secret DACL at create.src/paths.rs::write_secret via CreateFile2 + SECURITY_ATTRIBUTES (owner-only), fallback to fs::write + lock_down; Win32_Storage_FileSystem feature; DACL-at-create test.
Changed
Admission parity.ScanConfig::validate() now rejects non-routable custom CIDRs/endpoints (loopback/link-local/ULA/mapped-v6) and CDN-default port for WARP; CLI and server share one gate (3 new ConfigError variants).
View recompute batching.resultsView.svelte.ts dirty-flag + lazy getters; store.svelte.ts marks dirty on applyResult/setResults; ResultsTable.svelte single-pass $derived.by.
Font bundle. JetBrains Mono now woff2-only latin faces; Vazirmatn kept arabic/latin-ext/latin via unicode-range (≈107 KB dist reduction).
Fixed
SSE cap flake.sse_connection_cap_rejects_fifth_stream now uses 100 ms sleep + single 429 check (was polling with deadline); cargo test 3× green.
Import band-aids.src/server/mod.rs#[allow(unused_imports)] removed; test-only imports moved to tests.rs; clippy never_loop fixed.
Install cf-scanner 0.11.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.11.0/cf-scanner-installer.sh | sh
Phase-separated results (Pro). Candidates and Tunnel-test results
render as two independently sortable, filterable, and copyable lists —
side-by-side cards at ≥1024px, tabs below. Chip filters
(all / passing only / untested) with live counts; a dedicated
Copy-passing affordance; fail pills expose the redacted phase-2 error on
hover; a persistent "{passed} of {total} passed the real tunnel" summary.
Renamed the CDN verification tier to "Tunnel test" (آزمون تونل) so
"Verified" unambiguously means WARP keypair verification.
Verify banked while idle./api/status gains has_candidates; Pro
shows a "Verify banked (N)" action when candidates exist and tunnel
configs are set, running a phase2_only pass that PRESERVES the phase-1
list instead of wiping it (preserveResults freeze).
Compact targets. Preset amounts moved into the button labels
(Quick ~4K · Normal ~12K · Full 1.5M in Pro; per-mode amounts in Simple);
Custom reveals a single inline field replacing always-visible inputs and
hint paragraphs on both surfaces.
Changed
Phase-2 expert knobs (fragment / SNI / probe URLs) collapse into an
"Advanced tunnel settings" details card that force-opens when the server
routes errors to those fields; non-default choices show in the summary.
Install cf-scanner 0.10.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.10.0/cf-scanner-installer.sh | sh
Windows xray lifecycle on CI.tests/xray_lifecycle_windows.rs mirrors
the Unix subprocess suite with a rustc-compiled fake xray, so xray::spawn readiness, stop() kill, and trial-dir cleanup are now
covered on windows-latest as well as ubuntu-latest.
ADR-012 + SBOM. ADR-012 locks the review-scope calls (engine stays on api::types per ADR-011, serde(other) stays rejected). Release builds
now emit cf-scanner.spdx.json via cargo-sbom and upload it with the
global artifacts.
Changed
Frontend redesign ("Ethereal Glass"). OLED near-black paper with
violet/emerald mesh orbs; display font Space Grotesk Variable, body Plus
Jakarta Sans Variable (Inter removed); ultra-light 1.5px icon strokes;
double-bezel cards (outer shell + inset core) across Simple, Pro, results
tiles, and the results table; floating sticky glass header; button-in-
button CTAs with spring hover physics; scroll-entry reveals via
IntersectionObserver; progress bar animates scaleX (RTL-aware) instead
of width. Latin-only font subsets keep dist lean.
Server god-file split.src/server.rs (3.2k lines) is now src/server/{mod,state,error,guard,sse}.rs — router + handlers stay in mod.rs; state/Ranges, error envelope, guard/middleware, and SSE remain
isolated. No behavior change.
Data-dir single writer.paths::data_write_guard() serializes every
managed write (profiles.json now tmp+rename with 0o600 on the tmp file, ranges pool, identity.json, xray binary + sidecar). The gate is held
across the whole atomic write.
Library facade.src/lib.rs hides geo, socks, inline_verify as mod (crate-private); the public surface is exactly what the binary and
the integration tests import.
Fixed
CI: dtolnay/rust-toolchain@master drift broke the windows leg and then
ignored component inputs — toolchain is now selected by action ref
(@1.88) with explicit rustup component add steps. A bare env: that
broke release.yml parsing was removed.
Install cf-scanner 0.9.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.9.0/cf-scanner-installer.sh | sh
Ten-agent full-product review remediation: security hardening, engine
performance overhaul, API polish, CLI/UX, frontend a11y, and release-chain
integrity. All gates green (361 lib + 35 CLI + 8 integration tests, clippy -D warnings, cargo fmt, UI svelte-check strict, Playwright visual QA).
Added
CLI: grouped --help sections (candidate/stopping/tuning/phase2/WARP), --cap→max-probes and --target→stop-after aliases, --warp-wgconf
alias, serve --open (cross-platform browser launch), TTY-only live
progress ticker for one-shot scans, --json-errors machine-readable
failures, help examples block.
API: machine-readable code field on every error envelope; typed WARP
registration errors now map to proper statuses (timeout→504,
rate-limit→429, rejection/server→502) instead of blanket 502; xray
download failures use the uniform error envelope (no more HTTP 200 with {success:false}); SSE events carry an explicit retry: hint.
Resilience: SSE stream survives broadcast lag — it replays the last
terminal snapshot and keeps listening instead of closing (reconnect-storm
fix); UI re-hydrates status/results when EventSource reconnects and shows
an offline banner; EventSource handle is closed on teardown.
Changed
Engine throughput: per-worker task queues replace the shared
mutex-guarded receiver (dispatch no longer serializes); producer uses
backpressured send().await instead of a try_send/sleep poll; probe
futures race cancellation (select!) so Stop takes effect immediately
instead of after the in-flight timeout; result store flushes are O(1)
pushes with lazy sort-on-read (was O(found²) merge churn); broadcast
buffer 1024→4096; batch flush 64→256; per-port RNG hoisted out of hot
loops; phase-2 shares config/candidate sets via Arc across workers.
WARP: server public key resolved once per scan (was one identity.json
read PER PROBE); socket cache is per-controller and injectable, never
holds its lock across .await, and the global static is gone; corrupt
persisted identity keys log a warning before falling back to bundled.
Fetch stack: ranges + xray downloads share one reqwest client whose
redirect policy enforces the SSRF guard per hop; ~200 lines of hand-rolled
TLS/HTTP/chunked fetch code deleted; TCP_NODELAY on probe sockets;
wait-for-xray polling backs off exponentially; trial-dir sweeps throttle
to once per stale window and guard cleanup leaves the runtime thread.
Frontend: latin-only Inter subsets (-83 KB dist), first-invalid focus
management, aria-describedby wiring on all field errors, aria-sort on
sortable buttons, checkbox focus rings, copy feedback via role=status,
safe-area padding on the sticky action bar, live pace/ETA tick,
Copy-all respects active filters, tsconfig now strict.
Security
--warp-wgconf-file read capped at 64 KiB before parse (OOM guard).
Xray zip: archive and entry sizes capped at 64 MiB (zip-bomb guard);
cached-binary memo re-stats the file so a vanished/truncated binary
re-downloads instead of failing at spawn.
Origin: null requests denied (sandboxed-frame CSRF surface); JSON body
rejection text sanitized + truncated before echoing into error envelopes.
Contract tightening: deny_unknown_fields on ScanConfig/Phase2/Warp
payloads, custom WARP endpoints capped (2048), raw port-array precheck
before dedupe sort, decoded SIP002 user-id cap, wg URI host grammar check,
profile-name traversal characters rejected.
npm wrapper verifies the downloaded archive against its published .sha256 (fail closed), extracts via argv-form spawnSync (no shell
interpolation), retries downloads once, requires Node ≥14.14.
CI gains a version-parity job (Cargo.toml == npm package.json ==
RELEASE_TAG) and a pinned rust-toolchain (1.88 = CI toolchain).
Fixed
Ctrl+C hook failures are logged instead of silently leaving a scan
running (scan + wizard paths); wizard prompts moved off tokio workers
(spawn_blocking) and show a config summary before confirming.
Inline phase-2 verifier invariant violations return failed verdicts
instead of panicking a worker task; "every attempt failed" messages no
longer claim probes never ran when they simply did not pass; ephemeral
port errors carry their io::ErrorKind.
dgst parser is line-exact (SHA2-256= <hex>[ <filename>]) so a long hex
comment cannot satisfy the checksum; bundled range pools assert non-empty
at test time; Windows token-size query validates ERROR_INSUFFICIENT_BUFFER
before sizing its buffer.
Stale docs/comments corrected (profiles persistence, spec/intent frontend
reality notes, CHANGELOG newest-first order restored).
Install cf-scanner 0.8.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.8.0/cf-scanner-installer.sh | sh
Bilingual UI (English/Persian) with full RTL. Header language toggle
(persisted, html[dir/lang] applied pre-paint), Vazirmatn webfont,
logical-property sweep, LTR-isolated data tokens. Pro panel fully
translated (~120 keys).
Beginner mode upgrades: candidates-to-test knob alongside find-target,
copy → share-sheet → .txt export fallback chain, honest stop-overshoot
hint ("a few extra working IPs may land after the target").
Pro mode additions: ranges list-file import (server-grammar
validated, bare IPs classified per mode), AmneziaWG noise editor over the
pasted wgconf (plain INI or awg:// base64 URI round-trip, Off/Light/
Heavy presets, constraint validation per the 2026-08-23 research), and
Skip-to-Phase-2 — cancels phase 1 mid-scan and verifies the banked
candidates via phase2_only, with a low-yield suggestion badge.
WARP verify mode completes a full WireGuard session. Verify probes
now finish the cryptographic handshake under the user's keypair AND push
an encrypted DNS query through the tunnel, passing only on a data reply —
shape-only replies cannot tell a dummy-key handshake from a real one.
Discovery stays shape-only; verified scans badge their results.
Client-side validator module (ui/src/lib/validators.ts) mirroring
the server grammar: inline field errors gate both scan start and profile
save; pasted endpoint/CIDR lists normalize on blur (blank and duplicate
lines dropped); the ranges importer classifies with the same rules.
Results-table UX: tri-state aria-sort headers, 44 px touch targets,
bulk-copy live region, skeleton rows, filtered/true-empty states, and a
render cap for very large scans.
Grammar fixture (tests/fixtures/grammar-cases.json) pinning
CIDR/endpoint/SNI parsing for the server tests and the UI mirror.
Fixed
UI phase-2 starts were rejected (422). The form sent lowercase
fragment presets (off) while the API contract is Off/Light/… —
every UI-initiated phase-2 scan failed before it began. The form now maps
to the wire form (saved profiles keep loading) and fragment errors route
to the fragment field.
Pasting a wgconf now auto-enables real-keypair verification (previously
only the file-import path did), so verify-mode scans are never silently
run with the dummy key.
Mode flip no longer wipes restored port selections on hydration or
cross-mode profile loads; each import button targets its own field;
failed stop/cancel requests surface in the UI; ETAs humanize past 60 s.
Fixed
SSE /api/events no longer closes idle connections after replaying the
previous run's terminal: a browser EventSource held open between scans used
to reconnect-storm (connect -> replay -> close -> reconnect) and could miss
the next run entirely. A replayed terminal is now context only; the stream
ends on the next run's live terminal or an unrecoverable lag. Graceful
shutdown additionally bounds its wait (5 s grace) so deliberately-open idle
streams can never hang process exit.
Fixed
Stale validation message. "Fix the highlighted fields to continue."
no longer lingers after the user corrects the field — the message clears
live while typing and stays cleared after Next.
Forward tab clicks were silently swallowed until validation passed.
Tabs now navigate freely in both directions; validation errors paint on the
target step when it is shown.
Mode switch kept the previous mode's default port (e.g. 2408 carried
into CDN mode). The wizard now shows an inline amber warning when a custom
port differs from the mode default and never silently rewrites custom
values; untouched defaults still auto-correct (443 ↔ 2408).
Rate stat showed "0/s" when idle. The rate is now hidden whenever no
scan is running and appears only during a live scan.
"URIs" download option was offered when nothing could be exported
(WARP mode without configs). The option is now disabled in that state.
Heading hierarchy: "Generate WARP config (optional)" was an H4 under an H2
(skipped H3); it is now an H3.
Install cf-scanner 0.7.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.7.0/cf-scanner-installer.sh | sh
Rebuilt browser UI in Svelte 5, embedded via rust-embed (the vanilla
single-file page is gone). Simple mode by default: one-tap scanning with a
live progress bar, rate/ETA and top-endpoint cards. A Pro toggle reveals the
full console: complete scan configuration with inline validation and
field-level errors, profiles save/load/delete, phase-2 verification with an
xray availability chip + download, WARP identity registration (license +
overwrite consent) with wgconf export, sortable results table with per-row
importable-URI copy and copy-all, custom CIDR/exclusion editors, ranges
info, and a small-mode knob ("find up to N IPs").
Cloudflare port picker: checkbox chips from verified catalogs — CDN =
the six TLS ports from Cloudflare's network-ports documentation
(443/2053/2083/2087/2096/8443); WARP = the four official WireGuard ports
(2408/500/1701/4500) behind a collapsible 50-port community-verified
extended list — plus custom-port entry with inline validation.
Mobile/responsive pass: results table scrolls inside its card, ≥44px touch
targets on coarse pointers, 16px inputs below 640px (no iOS zoom-jump),
wrapping header, safe-area insets, scroll-jank-free background blooms.
Changed
SSE /api/events keeps idle connections open across scans: a replayed
terminal from the previous run is context only, so browser tabs stop
reconnect-storming while idle. Graceful shutdown bounds its wait (5 s) so
deliberately-open streams can never hang process exit.
Latency values use a dedicated green/amber/red ramp; brand orange now means
brand/actions only. Fonts ship bundled (Inter + JetBrains Mono) — the UI
makes zero CDN calls.
Fixed
Starting a scan no longer errors client-side with "Unexpected end of JSON
input" (202-with-empty-body responses are handled).
Idle EventSource connections closed-and-reconnected endlessly after
replaying a stale terminal, which could also miss the next run's events.
Copy affordances tell the truth: cards copy ip:port and say so; passing
phase-2 rows offer the real importable URI via /api/config/export.
Install cf-scanner 0.6.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/qmahyar/cf-scanner/releases/download/v0.6.0/cf-scanner-installer.sh | sh