From 95c7d86d96137f6b15f26e1f64a4cbe0bb8a7d5d Mon Sep 17 00:00:00 2001 From: ayuxsec Date: Sun, 4 Jan 2026 02:57:03 +0530 Subject: [PATCH 1/7] update(.gitignore): ignore all `resume.cfg` files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index da585383..3a7b66e2 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,5 @@ cmd/functional-test/*.cfg .devcontainer /httpx /dist -/resume.cfg +resume.cfg vendor/ \ No newline at end of file From f854b5794064d08adfe82f0f880b6532b88a1cb0 Mon Sep 17 00:00:00 2001 From: ayuxsec Date: Sun, 4 Jan 2026 04:33:47 +0530 Subject: [PATCH 2/7] =?UTF-8?q?fix(resume):=20-=20Remove=20`current`=20and?= =?UTF-8?q?=20`currentIndex`=20fields=20(commented=20out).=20=20=20-=20Add?= =?UTF-8?q?=20`completedIndex`=20(tracks=20number=20of=20results=20actuall?= =?UTF-8?q?y=20printed)=20and=20`lastPrinted`=20(stores=20the=20last=20hos?= =?UTF-8?q?t=20that=20was=20output).=20-=20Update=20resume=20logic=20in=20?= =?UTF-8?q?`Runner.RunEnumeration`:=20=20=20-=20Resume=20state=20is=20now?= =?UTF-8?q?=20updated=20**after**=20user=E2=80=91visible=20output=20is=20w?= =?UTF-8?q?ritten,=20preventing=20skipped=20targets=20on=20abrupt=20interr?= =?UTF-8?q?uption.=20=20=20-=20Record=20`lastPrinted`=20and=20increment=20?= =?UTF-8?q?`completedIndex`=20when=20a=20response=20is=20printed.=20-=20Di?= =?UTF-8?q?sable=20the=20old=20pre=E2=80=91output=20resume=20tracking=20in?= =?UTF-8?q?=20`processItem`=20(commented=20out).=20-=20Adjust=20`SaveResum?= =?UTF-8?q?eConfig`=20to=20persist=20the=20new=20fields=20(`completedIndex?= =?UTF-8?q?`=20and=20`lastPrinted`)=20instead=20of=20the=20removed=20ones.?= =?UTF-8?q?=20-=20Add=20detailed=20comment=20explaining=20the=20rationale?= =?UTF-8?q?=20for=20updating=20resume=20state=20at=20the=20latest=20commit?= =?UTF-8?q?=20point.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runner/resume.go | 10 ++++++---- runner/runner.go | 29 ++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/runner/resume.go b/runner/resume.go index ce2d3752..cb2d1aab 100644 --- a/runner/resume.go +++ b/runner/resume.go @@ -1,8 +1,10 @@ package runner type ResumeCfg struct { - ResumeFrom string - Index int - current string - currentIndex int + ResumeFrom string + Index int + // current string + // currentIndex int + completedIndex int // number of results actually printed + lastPrinted string // last host printed } diff --git a/runner/runner.go b/runner/runner.go index b708f6a4..af923258 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -1093,6 +1093,21 @@ func (r *Runner) RunEnumeration() { } if !r.options.DisableStdout && (!jsonOrCsv || jsonAndCsv || r.options.OutputAll) { + // NOTE: Resume state is updated at the *latest commit point* (i.e. after + // user-visible output is produced), not during input ingestion or scheduling. + // + // Reason: + // - Updating resume earlier (when reading from streamChan / processItem) + // causes skipped targets on resume if execution is interrupted. + // - A missed index is acceptable (user may re-scan one host), + // but a skipped index is catastrophic (user silently misses hosts). + // + // Therefore, we only advance resume state after the result has been + // irrevocably committed to output. + if r.options.resumeCfg != nil { + r.options.resumeCfg.lastPrinted = resp.Input + r.options.resumeCfg.completedIndex++ + } gologger.Silent().Msgf("%s\n", resp.str) } @@ -1287,11 +1302,11 @@ func (r *Runner) RunEnumeration() { processItem := func(k string) error { if r.options.resumeCfg != nil { - r.options.resumeCfg.current = k - r.options.resumeCfg.currentIndex++ - if r.options.resumeCfg.currentIndex <= r.options.resumeCfg.Index { - return nil - } + // r.options.resumeCfg.current = k + // r.options.resumeCfg.currentIndex++ + // if r.options.resumeCfg.currentIndex <= r.options.resumeCfg.Index { + // return nil + // } } protocol := r.options.protocol @@ -2581,8 +2596,8 @@ func extractPotentialFavIconsURLs(resp []byte) (candidates []string, baseHref st // SaveResumeConfig to file func (r *Runner) SaveResumeConfig() error { var resumeCfg ResumeCfg - resumeCfg.Index = r.options.resumeCfg.currentIndex - resumeCfg.ResumeFrom = r.options.resumeCfg.current + resumeCfg.Index = r.options.resumeCfg.completedIndex + resumeCfg.ResumeFrom = r.options.resumeCfg.lastPrinted return goconfig.Save(resumeCfg, DefaultResumeFile) } From d00d98c847fabaa6cc2d27af8cb2c5e39326b6c7 Mon Sep 17 00:00:00 2001 From: ayuxsec Date: Sun, 4 Jan 2026 05:31:37 +0530 Subject: [PATCH 3/7] fix(resume): Update resume tracking to run after output commit --- runner/runner.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/runner/runner.go b/runner/runner.go index af923258..2ce34cec 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -1093,24 +1093,25 @@ func (r *Runner) RunEnumeration() { } if !r.options.DisableStdout && (!jsonOrCsv || jsonAndCsv || r.options.OutputAll) { - // NOTE: Resume state is updated at the *latest commit point* (i.e. after - // user-visible output is produced), not during input ingestion or scheduling. - // - // Reason: - // - Updating resume earlier (when reading from streamChan / processItem) - // causes skipped targets on resume if execution is interrupted. - // - A missed index is acceptable (user may re-scan one host), - // but a skipped index is catastrophic (user silently misses hosts). - // - // Therefore, we only advance resume state after the result has been - // irrevocably committed to output. - if r.options.resumeCfg != nil { - r.options.resumeCfg.lastPrinted = resp.Input - r.options.resumeCfg.completedIndex++ - } gologger.Silent().Msgf("%s\n", resp.str) } + // NOTE: Resume state is updated at the *latest commit point* (i.e. after + // user-visible output is produced), not during input ingestion or scheduling. + // + // Reason: + // - Updating resume earlier (when reading from streamChan / processItem) + // causes skipped targets on resume if execution is interrupted. + // - A missed index is acceptable (user may re-scan one host), + // but a skipped index is catastrophic (user silently misses hosts). + // + // Therefore, we only advance resume state after the result has been + // irrevocably committed to output. + if r.options.resumeCfg != nil { + r.options.resumeCfg.lastPrinted = resp.Input + r.options.resumeCfg.completedIndex++ + } + // store responses or chain in directory if resp.Err == nil { URL, _ := urlutil.Parse(resp.URL) From 93a00fc94d7d9fadcce3f9c3bf836733ebc70c4f Mon Sep 17 00:00:00 2001 From: ayuxsec <249073442+ayuxsec@users.noreply.github.com> Date: Tue, 6 Jan 2026 05:45:48 +0530 Subject: [PATCH 4/7] Update runner/resume.go Co-authored-by: Nakul Bharti --- runner/resume.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runner/resume.go b/runner/resume.go index cb2d1aab..5be80752 100644 --- a/runner/resume.go +++ b/runner/resume.go @@ -5,6 +5,6 @@ type ResumeCfg struct { Index int // current string // currentIndex int - completedIndex int // number of results actually printed - lastPrinted string // last host printed + completedIndex int + lastPrinted string } From 4ebad2e2723be722e35f5216f32dc77749805c30 Mon Sep 17 00:00:00 2001 From: ayuxsec Date: Tue, 6 Jan 2026 06:28:35 +0530 Subject: [PATCH 5/7] refactor(resume): fix `resume_from field`; keep existing buggy index logic unchanged --- runner/resume.go | 12 ++++++------ runner/runner.go | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/runner/resume.go b/runner/resume.go index 5be80752..c70c49c8 100644 --- a/runner/resume.go +++ b/runner/resume.go @@ -1,10 +1,10 @@ package runner type ResumeCfg struct { - ResumeFrom string - Index int - // current string - // currentIndex int - completedIndex int - lastPrinted string + ResumeFrom string + Index int + current string + currentIndex int + // completedIndex int + lastPrinted string } diff --git a/runner/runner.go b/runner/runner.go index 2ce34cec..28fa8342 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -1109,7 +1109,7 @@ func (r *Runner) RunEnumeration() { // irrevocably committed to output. if r.options.resumeCfg != nil { r.options.resumeCfg.lastPrinted = resp.Input - r.options.resumeCfg.completedIndex++ + // r.options.resumeCfg.completedIndex++ } // store responses or chain in directory @@ -1303,11 +1303,11 @@ func (r *Runner) RunEnumeration() { processItem := func(k string) error { if r.options.resumeCfg != nil { - // r.options.resumeCfg.current = k - // r.options.resumeCfg.currentIndex++ - // if r.options.resumeCfg.currentIndex <= r.options.resumeCfg.Index { - // return nil - // } + r.options.resumeCfg.current = k + r.options.resumeCfg.currentIndex++ + if r.options.resumeCfg.currentIndex <= r.options.resumeCfg.Index { + return nil + } } protocol := r.options.protocol @@ -2597,7 +2597,7 @@ func extractPotentialFavIconsURLs(resp []byte) (candidates []string, baseHref st // SaveResumeConfig to file func (r *Runner) SaveResumeConfig() error { var resumeCfg ResumeCfg - resumeCfg.Index = r.options.resumeCfg.completedIndex + resumeCfg.Index = r.options.resumeCfg.currentIndex resumeCfg.ResumeFrom = r.options.resumeCfg.lastPrinted return goconfig.Save(resumeCfg, DefaultResumeFile) } From 24b4efe4573132ff281e68436cf1b565a570ce5a Mon Sep 17 00:00:00 2001 From: ayuxsec Date: Tue, 6 Jan 2026 06:33:11 +0530 Subject: [PATCH 6/7] refactor(runner): update comments --- runner/resume.go | 3 +-- runner/runner.go | 12 +----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/runner/resume.go b/runner/resume.go index c70c49c8..e489dd07 100644 --- a/runner/resume.go +++ b/runner/resume.go @@ -5,6 +5,5 @@ type ResumeCfg struct { Index int current string currentIndex int - // completedIndex int - lastPrinted string + lastPrinted string } diff --git a/runner/runner.go b/runner/runner.go index 28fa8342..e16f1171 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -1096,17 +1096,7 @@ func (r *Runner) RunEnumeration() { gologger.Silent().Msgf("%s\n", resp.str) } - // NOTE: Resume state is updated at the *latest commit point* (i.e. after - // user-visible output is produced), not during input ingestion or scheduling. - // - // Reason: - // - Updating resume earlier (when reading from streamChan / processItem) - // causes skipped targets on resume if execution is interrupted. - // - A missed index is acceptable (user may re-scan one host), - // but a skipped index is catastrophic (user silently misses hosts). - // - // Therefore, we only advance resume state after the result has been - // irrevocably committed to output. + // Update resume state after output to avoid skipping unprocessed targets on interrupt if r.options.resumeCfg != nil { r.options.resumeCfg.lastPrinted = resp.Input // r.options.resumeCfg.completedIndex++ From 540578780a3ec665d980d10ca92c25c690052852 Mon Sep 17 00:00:00 2001 From: ayuxsec <249073442+ayuxsec@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:58:09 +0530 Subject: [PATCH 7/7] Update runner/runner.go Co-authored-by: Nakul Bharti --- runner/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runner/runner.go b/runner/runner.go index e16f1171..f64aef91 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -1099,7 +1099,7 @@ func (r *Runner) RunEnumeration() { // Update resume state after output to avoid skipping unprocessed targets on interrupt if r.options.resumeCfg != nil { r.options.resumeCfg.lastPrinted = resp.Input - // r.options.resumeCfg.completedIndex++ + } // store responses or chain in directory