Skip to content

fix(store,logger,scan): concurrency-adjacent races and correctness fixes#343

Open
TBX3D wants to merge 4 commits into
vmfunc:mainfrom
TBX3D:harden-concurrency-portable
Open

fix(store,logger,scan): concurrency-adjacent races and correctness fixes#343
TBX3D wants to merge 4 commits into
vmfunc:mainfrom
TBX3D:harden-concurrency-portable

Conversation

@TBX3D

@TBX3D TBX3D commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

a handful of fixes surfaced while working on -concurrency: two targets
whose sanitized names collided (differing only in separator punctuation)
could silently overwrite each other's state snapshot, so the snapshot
path now appends a hash of the full, un-sanitized target to stay
injective, and Save writes through a temp file + rename so a reader never
observes a partially-written snapshot. logger.CreateFile/Write now flatten
a URL's path into the filename instead of keeping the '/' verbatim, which
previously tried (and failed) to open a file under a directory that was
never created. a couple of scan/module status lines that bypassed the
output sink via a raw fmt.Println are routed through it, since only the
sink is lock-wrapped under -concurrency>1.

TBX3D added 3 commits July 9, 2026 14:24
CreateFile/Write kept the '/' from a target's url path when building the
log filename, so a target like http://host:port/ tried to open
<dir>/host:port/.log whose parent directory never existed. os.OpenFile
failed and scanTarget aborted the whole target before any scanning ran.

fold every '/' and '\\' run in the sanitized url into a single '_' via a
shared logPath helper so CreateFile and Write always resolve to the same
flat file, and mkdir the parent defensively in getWriter as a second line
of defense.
subdomaintakeover, cloudstorage, and the next.js framework detector
printed their status/error lines with a bare fmt.Println straight to
os.Stdout, bypassing the output package's sink. under -concurrency>1
only the sink is lock-wrapped, so these lines could interleave or tear
against lines other targets write concurrently.

swap them for output.ScanStart / output.Error, matching every other
scanner in the package.
sanitize() folds every separator run (and a literal '_') to one '_', so
distinct targets like https://a.com/x and https://a.com//x, or
host:8443/path and host_8443_path, sanitized to the identical string and
shared one snapshot file - the second target's Save silently clobbered
the first's baseline. Save's os.WriteFile also wasn't atomic, so two
targets racing on a collided path (or -concurrency>1 in general) could
interleave partial writes.

pathFor now appends 16 hex chars of the target's sha256 to the sanitized
name, so distinct targets never collide, and Save writes through a temp
file in the same dir followed by os.Rename so a reader always sees a
complete snapshot or the previous one, never a partial write.

this changes the on-disk snapshot filename scheme: existing users' -diff
baselines under the old sanitize()-only names won't be found and the
next run re-baselines from scratch (every finding reports as newly
added once). noting this as a deliberate tradeoff for a local hardening
pass rather than silently orphaning them.
@TBX3D
TBX3D requested a review from vmfunc as a code owner July 9, 2026 21:24
@github-actions github-actions Bot added size/l <500 lines changed scan changes to scan engine modules changes to scan modules tests test changes and removed size/l <500 lines changed labels Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

pr summary

9 files changed (+379 -15)

category files
go source 9
tests 4

@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 61.36364% with 17 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@7ea1cd2). Learn more about missing BASE report.

Files with missing lines Patch % Lines
internal/store/store.go 44.44% 5 Missing and 5 partials ⚠️
internal/scan/js/frameworks/next.go 0.00% 3 Missing ⚠️
internal/logger/logger.go 90.47% 1 Missing and 1 partial ⚠️
internal/scan/cloudstorage.go 0.00% 1 Missing ⚠️
internal/scan/subdomaintakeover.go 0.00% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #343   +/-   ##
=======================================
  Coverage        ?   54.79%           
=======================================
  Files           ?       81           
  Lines           ?     6908           
  Branches        ?        0           
=======================================
  Hits            ?     3785           
  Misses          ?     2848           
  Partials        ?      275           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TBX3D
TBX3D force-pushed the harden-concurrency-portable branch from 31b29c2 to 7c479ed Compare July 9, 2026 21:52
@github-actions github-actions Bot added the size/l <500 lines changed label Jul 9, 2026
golangci-lint (errcheck) flagged the bare defer os.Remove(tmpPath) in
writeFileAtomic; wrap it so the discard is explicit.
@TBX3D
TBX3D force-pushed the harden-concurrency-portable branch from 7c479ed to f5388d8 Compare July 9, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

modules changes to scan modules scan changes to scan engine size/l <500 lines changed tests test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants