Skip to content

release interactsh v1.3.1#1343

Merged
dogancanbakir merged 20 commits into
mainfrom
dev
Mar 10, 2026
Merged

release interactsh v1.3.1#1343
dogancanbakir merged 20 commits into
mainfrom
dev

Conversation

@dogancanbakir

@dogancanbakir dogancanbakir commented Mar 5, 2026

Copy link
Copy Markdown
Member

closes #1342

Summary by CodeRabbit

  • Chores

    • Bumped project version to 1.3.1 and updated several dependencies.
    • Added configurable max shared interactions via INTERACTSH_MAX_SHARED_INTERACTIONS (default 10000); docs updated with wildcard-mode guidance.
  • New Features

    • Per-zone custom DNS records (A/AAAA/CNAME/MX/TXT/NS) with ACME TXT support.
    • Per-consumer interaction polling and consumer removal for finer read offsets.
  • Bug Fixes

    • Trimmed decrypted payloads before parsing; improved TLS protocol detection and stale-data cleanup on re-registration.
  • Tests

    • Added extensive storage, encryption, DNS and protocol round-trip tests.

Vasco-jofra and others added 17 commits November 11, 2025 15:54
Bumps [github.com/projectdiscovery/gologger](https://github.com/projectdiscovery/gologger) from 1.1.67 to 1.1.68.
- [Release notes](https://github.com/projectdiscovery/gologger/releases)
- [Commits](projectdiscovery/gologger@v1.1.67...v1.1.68)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/gologger
  dependency-version: 1.1.68
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [github.com/projectdiscovery/retryablehttp-go](https://github.com/projectdiscovery/retryablehttp-go) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/projectdiscovery/retryablehttp-go/releases)
- [Commits](projectdiscovery/retryablehttp-go@v1.3.5...v1.3.6)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/retryablehttp-go
  dependency-version: 1.3.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/projectdiscovery/fastdialer/releases)
- [Commits](projectdiscovery/fastdialer@v0.5.3...v0.5.4)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/fastdialer
  dependency-version: 0.5.4
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…dev/github.com/projectdiscovery/fastdialer-0.5.4

chore(deps): bump github.com/projectdiscovery/fastdialer from 0.5.3 to 0.5.4
…dev/github.com/projectdiscovery/retryablehttp-go-1.3.6

chore(deps): bump github.com/projectdiscovery/retryablehttp-go from 1.3.5 to 1.3.6
…dev/github.com/projectdiscovery/gologger-1.1.68

chore(deps): bump github.com/projectdiscovery/gologger from 1.1.67 to 1.1.68
Bumps [github.com/projectdiscovery/hmap](https://github.com/projectdiscovery/hmap) from 0.0.99 to 0.0.100.
- [Release notes](https://github.com/projectdiscovery/hmap/releases)
- [Commits](projectdiscovery/hmap@v0.0.99...v0.0.100)

---
updated-dependencies:
- dependency-name: github.com/projectdiscovery/hmap
  dependency-version: 0.0.100
  dependency-type: indirect
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…dev/github.com/projectdiscovery/hmap-0.0.100

chore(deps): bump github.com/projectdiscovery/hmap from 0.0.99 to 0.0.100
…l-differentiation

fix: differentiate HTTP vs HTTPS protocol in interactions
In wildcard mode, GetInteractionsWithId clears data on first poll,
starving subsequent clients. Use per-consumer read offsets so each
client independently tracks its position in the shared data buffer.

Buffer cap is configurable via INTERACTSH_MAX_SHARED_INTERACTIONS
env var (default 10000).
…hal-corruption

fix: interaction data corruption causing unmarshal errors
@dogancanbakir dogancanbakir self-assigned this Mar 5, 2026
@neo-by-projectdiscovery-dev

neo-by-projectdiscovery-dev Bot commented Mar 5, 2026

Copy link
Copy Markdown

Neo - PR Security Review

No security issues found

Highlights

  • Adds custom DNS records feature with YAML-based configuration supporting A, AAAA, CNAME, MX, TXT, and NS record types
  • Implements OnResult callback support for root TLD DNS interactions to enable real-time event processing
  • Adds comprehensive test coverage for custom DNS record handling including IPv4, IPv6, and mixed scenarios
  • Documents INTERACTSH_MAX_SHARED_INTERACTIONS environment variable for wildcard mode buffer configuration

Comment @pdneo help for available commands. · Open in Neo

@coderabbitai

coderabbitai Bot commented Mar 5, 2026

Copy link
Copy Markdown

Walkthrough

Bumps package and dependency versions, trims decrypted payloads before JSON unmarshalling, refactors servers to use jsoniter.Marshal and dynamic http/https detection, adds per-consumer storage APIs and eviction/compaction logic, extensive storage and round‑trip tests, and docs + env var for MaxSharedInteractions.

Changes

Cohort / File(s) Summary
Version & deps
go.mod, pkg/options/utils.go
Bumped dependency versions and package Version to "1.3.1".
Client
pkg/client/client.go
Trim trailing whitespace from decrypted interaction payloads before JSON unmarshal.
Server JSON encoding
pkg/server/...
pkg/server/dns_server.go, pkg/server/ftp_server.go, pkg/server/ldap_server.go, pkg/server/responder_server.go, pkg/server/smb_server.go, pkg/server/smtp_server.go
Replaced bytes.Buffer+jsoniter.Encoder with jsoniter.Marshal; log marshaled bytes and store them directly; removed unused bytes imports.
HTTP server & tests
pkg/server/http_server.go, pkg/server/http_server_test.go
Added httpProtocol(r *http.Request) (http/https detection); updated handleInteraction signature to accept *http.Request; refactored JSON marshaling; added tests for protocol detection; added consumer cleanup on deregistration.
DNS custom records
pkg/server/dns_server.go, pkg/server/dns_server_test.go
Introduced CustomRecordConfig and DNSRecordsConfig, changed custom records storage to []CustomRecordConfig, added typed record resolution (A/AAAA/CNAME/MX/TXT/NS) and addCustomRecordToMessage workflow; tests updated for typed queries.
Storage API & options
pkg/storage/storage.go, pkg/storage/option.go, cmd/interactsh-server/main.go
Added GetInteractionsWithIdForConsumer and RemoveConsumer to Storage interface; introduced MaxSharedInteractions option with default and server env var override INTERACTSH_MAX_SHARED_INTERACTIONS.
Storage internals
pkg/storage/storagedb.go, pkg/storage/types.go
Implemented per-consumer ReadOffsets and LastSeen, cache-removal -> LevelDB deletion, disk cleanup on re-registration, trimming/eviction/compaction logic, and consumer lifecycle helpers (GetInteractionsWithIdForConsumer, RemoveConsumer).
Storage tests
pkg/storage/roundtrip_test.go, pkg/storage/storagedb_test.go
Added extensive end-to-end encryption/storage round-trip tests and consumer-focused tests covering unseen reads, eviction, compaction, trailing-newline handling, JSON control-character escaping, and LevelDB cleanup.
Docs
README.md
Updated docs: default server rename, wildcard-mode buffering explanation, INTERACTSH_MAX_SHARED_INTERACTIONS example, and cloud metadata host updates.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Server
  participant StorageDB
  participant LevelDB

  Client->>Server: Poll or submit interaction (id, consumerID)
  Server->>StorageDB: GetInteractionsWithIdForConsumer(id, consumerID)
  StorageDB->>StorageDB: Check ReadOffsets / LastSeen, applyTrim/evict if needed
  alt interactions in memory cache
    StorageDB-->>Server: Return unseen interactions (bytes)
  else interactions persisted
    StorageDB->>LevelDB: Read persisted entries for id
    LevelDB-->>StorageDB: Return bytes
    StorageDB-->>Server: Return unseen interactions (bytes)
  end
  Server->>Client: Respond with interactions
  Note right of StorageDB: RemoveConsumer(id, consumerID) compacts/cleans stored data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
I hopped through bytes and nibbled a stray newline,
Trimmed the crumbs, marshaled data till it shone fine.
HTTPS whispers when TLS draws near,
Consumers tidy up — eviction is clear! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'release interactsh v1.3.1' accurately describes the primary change—a version bump release—matching the version update in pkg/options/utils.go and the PR objectives.
Linked Issues check ✅ Passed The pull request addresses issue #1342 regarding releasing interactsh v1.3.1. Changes include version bump, dependency updates, DNS record enhancements, storage improvements, and documentation updates aligned with release objectives.
Out of Scope Changes check ✅ Passed All changes appear directly related to the v1.3.1 release: version/dependency updates, feature enhancements (DNS custom records, per-consumer storage), bug fixes (JSON encoding, trailing whitespace), tests, and documentation updates consistent with a release.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands and usage tips.

@dogancanbakir
dogancanbakir requested a review from knakul853 March 5, 2026 11:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
pkg/storage/roundtrip_test.go (1)

95-195: Consider extracting shared round-trip test logic.

TestFullRoundTripInMemory and TestFullRoundTripDisk duplicate most setup/store/retrieve/assert code. A shared helper would reduce maintenance overhead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/storage/roundtrip_test.go` around lines 95 - 195, Both tests duplicate
setup/store/retrieve/assert logic; extract a helper to run the common round-trip
flow. Create a helper (e.g., runFullRoundTrip(t, store)) that accepts an object
implementing the minimal interface used in the tests (methods: SetIDPublicKey,
AddInteraction, GetInteractions, Close) or accept a constructor func that
returns the storage from Options/New; move key generation, secret/correlationID
creation, loop that marshals + AddInteraction, the GetInteractions/assertions,
and the decryption/unmarshal/assert loop (using clientDecrypt) into that helper,
then have TestFullRoundTripInMemory and TestFullRoundTripDisk call it with the
in-memory instance (New(&Options{...})) and disk instance respectively.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/server/responder_server.go`:
- Around line 95-97: The warning log incorrectly says "dns interaction"; update
the log message in the Responder code path where AddInteractionWithId is called
(the gologger.Warning().Msgf call that follows
h.options.Storage.AddInteractionWithId(h.options.Token, data)) to use the
correct protocol label such as "responder interaction" (or "Responder
interaction") so operators see the correct source of the failure; leave the rest
of the call (including formatting of err) unchanged.

In `@pkg/server/smb_server.go`:
- Around line 107-109: The warning log in the SMB handler incorrectly refers to
"dns interaction"; update the message to reference SMB so logs are accurate:
change the gologger.Warning().Msgf call that follows
h.options.Storage.AddInteractionWithId(h.options.Token, data) to a message such
as "Could not store SMB interaction" (include the error value as before) so the
log accurately reflects the protocol and still includes the error details.

In `@pkg/storage/storagedb.go`:
- Around line 124-129: The delete call in the re-registration flow currently
swallows errors (s.db.Delete([]byte(correlationID), nil)), leaving stale
ciphertext on disk; modify the re-registration logic in storagedb.go to capture
the error returned by s.db.Delete, and handle it explicitly—either return the
error up the call stack or log it and fail the registration flow so callers know
disk cleanup failed; reference the UseDisk() check, the s.db.Delete call, and
the correlationID variable when locating the code to change.

---

Nitpick comments:
In `@pkg/storage/roundtrip_test.go`:
- Around line 95-195: Both tests duplicate setup/store/retrieve/assert logic;
extract a helper to run the common round-trip flow. Create a helper (e.g.,
runFullRoundTrip(t, store)) that accepts an object implementing the minimal
interface used in the tests (methods: SetIDPublicKey, AddInteraction,
GetInteractions, Close) or accept a constructor func that returns the storage
from Options/New; move key generation, secret/correlationID creation, loop that
marshals + AddInteraction, the GetInteractions/assertions, and the
decryption/unmarshal/assert loop (using clientDecrypt) into that helper, then
have TestFullRoundTripInMemory and TestFullRoundTripDisk call it with the
in-memory instance (New(&Options{...})) and disk instance respectively.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e3918008-10b2-4d4f-a428-7bca72f1090b

📥 Commits

Reviewing files that changed from the base of the PR and between 307efeb and 4573152.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (13)
  • go.mod
  • pkg/client/client.go
  • pkg/options/utils.go
  • pkg/server/dns_server.go
  • pkg/server/ftp_server.go
  • pkg/server/http_server.go
  • pkg/server/http_server_test.go
  • pkg/server/ldap_server.go
  • pkg/server/responder_server.go
  • pkg/server/smb_server.go
  • pkg/server/smtp_server.go
  • pkg/storage/roundtrip_test.go
  • pkg/storage/storagedb.go

Comment on lines +95 to 97
gologger.Debug().Msgf("Responder Interaction: \n%s\n", string(data))
if err := h.options.Storage.AddInteractionWithId(h.options.Token, data); err != nil {
gologger.Warning().Msgf("Could not store dns interaction: %s\n", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Correct protocol label in warning message.

Line 97 currently logs "dns interaction" from Responder code path, which can mislead operations.

Proposed fix
-							gologger.Warning().Msgf("Could not store dns interaction: %s\n", err)
+							gologger.Warning().Msgf("Could not store responder interaction: %s\n", err)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
gologger.Debug().Msgf("Responder Interaction: \n%s\n", string(data))
if err := h.options.Storage.AddInteractionWithId(h.options.Token, data); err != nil {
gologger.Warning().Msgf("Could not store dns interaction: %s\n", err)
gologger.Debug().Msgf("Responder Interaction: \n%s\n", string(data))
if err := h.options.Storage.AddInteractionWithId(h.options.Token, data); err != nil {
gologger.Warning().Msgf("Could not store responder interaction: %s\n", err)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/responder_server.go` around lines 95 - 97, The warning log
incorrectly says "dns interaction"; update the log message in the Responder code
path where AddInteractionWithId is called (the gologger.Warning().Msgf call that
follows h.options.Storage.AddInteractionWithId(h.options.Token, data)) to use
the correct protocol label such as "responder interaction" (or "Responder
interaction") so operators see the correct source of the failure; leave the rest
of the call (including formatting of err) unchanged.

Comment thread pkg/server/smb_server.go
Comment on lines +107 to 109
gologger.Debug().Msgf("SMB Interaction: \n%s\n", string(data))
if err := h.options.Storage.AddInteractionWithId(h.options.Token, data); err != nil {
gologger.Warning().Msgf("Could not store dns interaction: %s\n", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix incorrect protocol name in error log.

Line 109 says "dns interaction" inside SMB handling, which is misleading during incident/debug analysis.

Proposed fix
-							gologger.Warning().Msgf("Could not store dns interaction: %s\n", err)
+							gologger.Warning().Msgf("Could not store smb interaction: %s\n", err)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
gologger.Debug().Msgf("SMB Interaction: \n%s\n", string(data))
if err := h.options.Storage.AddInteractionWithId(h.options.Token, data); err != nil {
gologger.Warning().Msgf("Could not store dns interaction: %s\n", err)
gologger.Debug().Msgf("SMB Interaction: \n%s\n", string(data))
if err := h.options.Storage.AddInteractionWithId(h.options.Token, data); err != nil {
gologger.Warning().Msgf("Could not store smb interaction: %s\n", err)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/smb_server.go` around lines 107 - 109, The warning log in the SMB
handler incorrectly refers to "dns interaction"; update the message to reference
SMB so logs are accurate: change the gologger.Warning().Msgf call that follows
h.options.Storage.AddInteractionWithId(h.options.Token, data) to a message such
as "Could not store SMB interaction" (include the error value as before) so the
log accurately reflects the protocol and still includes the error details.

Comment thread pkg/storage/storagedb.go
Comment on lines +124 to +129
// Clear any stale data from a previous registration (e.g. after cache eviction
// and session restore). Old data would be encrypted with a different AES key
// and cause decryption failures on the client.
if s.Options.UseDisk() {
_ = s.db.Delete([]byte(correlationID), nil)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C2 's\.db\.Delete\(' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 689


🏁 Script executed:

#!/bin/bash
sed -n '115,135p' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 702


🏁 Script executed:

#!/bin/bash
sed -n '100,135p' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 1249


🏁 Script executed:

#!/bin/bash
sed -n '90,105p' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 646


🏁 Script executed:

#!/bin/bash
rg -n 'func \(s \*StorageDB\) Get' pkg/storage/storagedb.go | head -5

Repository: projectdiscovery/interactsh

Length of output: 390


🏁 Script executed:

#!/bin/bash
sed -n '274,300p' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 790


🏁 Script executed:

#!/bin/bash
sed -n '220,275p' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 1808


🏁 Script executed:

#!/bin/bash
sed -n '249,270p' pkg/storage/storagedb.go

Repository: projectdiscovery/interactsh

Length of output: 709


Handle stale-data delete failures explicitly in re-registration flow.

On line 128, delete errors are swallowed. If disk cleanup fails during re-registration, stale ciphertext encrypted with the previous AES key remains on disk. On cache eviction and restore, this stale data gets read and causes decryption/unmarshal failures on the client, as noted in the preceding comment.

Proposed fix
 	// Clear any stale data from a previous registration (e.g. after cache eviction
 	// and session restore). Old data would be encrypted with a different AES key
 	// and cause decryption failures on the client.
 	if s.Options.UseDisk() {
-		_ = s.db.Delete([]byte(correlationID), nil)
+		if err := s.db.Delete([]byte(correlationID), nil); err != nil && !errors.Is(err, leveldb.ErrNotFound) {
+			return errors.Wrap(err, "could not clear stale interaction data")
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/storage/storagedb.go` around lines 124 - 129, The delete call in the
re-registration flow currently swallows errors
(s.db.Delete([]byte(correlationID), nil)), leaving stale ciphertext on disk;
modify the re-registration logic in storagedb.go to capture the error returned
by s.db.Delete, and handle it explicitly—either return the error up the call
stack or log it and fail the registration flow so callers know disk cleanup
failed; reference the UseDisk() check, the s.db.Delete call, and the
correlationID variable when locating the code to change.

…ent-poll

fix: wildcard server sends interactions to all clients

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/storage/storagedb.go (1)

131-133: ⚠️ Potential issue | 🟠 Major

Handle stale-data cleanup errors in re-registration path.

Line 132 still ignores delete failures. If cleanup fails, stale data can persist and poll responses may include incompatible ciphertext.

Proposed patch
 if s.Options.UseDisk() {
-	_ = s.db.Delete([]byte(correlationID), nil)
+	if err := s.db.Delete([]byte(correlationID), nil); err != nil {
+		return errors.Wrap(err, "could not clear stale interaction data")
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/storage/storagedb.go` around lines 131 - 133, The delete call for stale
data currently swallows errors; update the re-registration cleanup in
storagedb.go so the result of s.db.Delete([]byte(correlationID), nil) is checked
and handled: capture the returned error from s.db.Delete, and either log it via
the component logger or return it up the call path (depending on the surrounding
function's error semantics) so failures to remove stale ciphertext are not
ignored; look for the block that checks s.Options.UseDisk() and the symbols
s.db.Delete and correlationID to locate and modify the code.
🧹 Nitpick comments (2)
cmd/interactsh-server/main.go (1)

257-261: Surface invalid INTERACTSH_MAX_SHARED_INTERACTIONS values.

Right now invalid/non-positive env values are ignored silently, which makes config mistakes easy to miss. A warning keeps behavior safe while improving operability.

Proposed patch
 if v := os.Getenv("INTERACTSH_MAX_SHARED_INTERACTIONS"); v != "" {
-	if n, err := strconv.Atoi(v); err == nil && n > 0 {
-		storeOptions.MaxSharedInteractions = n
-	}
+	n, err := strconv.Atoi(v)
+	if err != nil || n <= 0 {
+		gologger.Warning().Msgf("Ignoring invalid INTERACTSH_MAX_SHARED_INTERACTIONS=%q (must be a positive integer)", v)
+	} else {
+		storeOptions.MaxSharedInteractions = n
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/interactsh-server/main.go` around lines 257 - 261, The code silently
ignores invalid or non-positive INTERACTSH_MAX_SHARED_INTERACTIONS values;
update the block that parses os.Getenv("INTERACTSH_MAX_SHARED_INTERACTIONS") so
that when strconv.Atoi returns an error or the parsed n <= 0 you emit a warning
(e.g., via log.Printf or the existing logger) indicating the invalid value and
that the default will be used, and only set storeOptions.MaxSharedInteractions
when err == nil && n > 0; reference the env var name and the
storeOptions.MaxSharedInteractions field in the message so it's easy to trace.
pkg/server/http_server.go (1)

426-433: Consider surfacing consumer cleanup failures during deregistration.

RemoveConsumer errors are currently discarded. Logging at least warnings would make cleanup issues diagnosable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/http_server.go` around lines 426 - 433, Replace the ignored
RemoveConsumer calls with error handling: assign the result of
h.options.Storage.RemoveConsumer(...) to err, and if err != nil emit a warning
that includes the domain or token and r.CorrelationID so failures are
diagnosable; use your existing logger (e.g., h.logger or h.options.Logger) to
call Warn/Warnf with a message like "failed removing consumer <domain|token> for
correlation <id>: <err>" and fall back to log.Printf if no logger exists. Ensure
you do this for both the loop over h.options.Domains (when h.options.RootTLD)
and the h.options.Token path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/server/http_server.go`:
- Around line 468-477: The code is silently ignoring errors from storage calls
in the poll flow: update the blocks that call
h.options.Storage.GetInteractionsWithIdForConsumer (used for root TLDs and for
h.options.Token) to check the returned error instead of discarding it; if an
error is returned, propagate it (return it from the surrounding
handler/function) or at minimum log it and return an appropriate error response
so backend failures do not appear as partial success — update references to ID,
tlddata, extradata, and the h.options.RootTLD/h.options.Token branches to handle
and surface the error from GetInteractionsWithIdForConsumer.

In `@README.md`:
- Around line 705-708: Change the fenced code block from using the "console"
language with "$" prompts to a "bash" block without "$" prefixes: remove the
leading "$ " on the two lines referencing the INTERACTSH_MAX_SHARED_INTERACTIONS
environment variable and the interactsh-server command, and update the block
fence language from "console" to "bash" so the block becomes a plain bash
snippet (affecting the lines that set INTERACTSH_MAX_SHARED_INTERACTIONS and
call interactsh-server).

---

Duplicate comments:
In `@pkg/storage/storagedb.go`:
- Around line 131-133: The delete call for stale data currently swallows errors;
update the re-registration cleanup in storagedb.go so the result of
s.db.Delete([]byte(correlationID), nil) is checked and handled: capture the
returned error from s.db.Delete, and either log it via the component logger or
return it up the call path (depending on the surrounding function's error
semantics) so failures to remove stale ciphertext are not ignored; look for the
block that checks s.Options.UseDisk() and the symbols s.db.Delete and
correlationID to locate and modify the code.

---

Nitpick comments:
In `@cmd/interactsh-server/main.go`:
- Around line 257-261: The code silently ignores invalid or non-positive
INTERACTSH_MAX_SHARED_INTERACTIONS values; update the block that parses
os.Getenv("INTERACTSH_MAX_SHARED_INTERACTIONS") so that when strconv.Atoi
returns an error or the parsed n <= 0 you emit a warning (e.g., via log.Printf
or the existing logger) indicating the invalid value and that the default will
be used, and only set storeOptions.MaxSharedInteractions when err == nil && n >
0; reference the env var name and the storeOptions.MaxSharedInteractions field
in the message so it's easy to trace.

In `@pkg/server/http_server.go`:
- Around line 426-433: Replace the ignored RemoveConsumer calls with error
handling: assign the result of h.options.Storage.RemoveConsumer(...) to err, and
if err != nil emit a warning that includes the domain or token and
r.CorrelationID so failures are diagnosable; use your existing logger (e.g.,
h.logger or h.options.Logger) to call Warn/Warnf with a message like "failed
removing consumer <domain|token> for correlation <id>: <err>" and fall back to
log.Printf if no logger exists. Ensure you do this for both the loop over
h.options.Domains (when h.options.RootTLD) and the h.options.Token path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 031d7127-ba55-4320-9a9b-6356ff9ac1ce

📥 Commits

Reviewing files that changed from the base of the PR and between 4573152 and fe9869d.

📒 Files selected for processing (8)
  • README.md
  • cmd/interactsh-server/main.go
  • pkg/server/http_server.go
  • pkg/storage/option.go
  • pkg/storage/storage.go
  • pkg/storage/storagedb.go
  • pkg/storage/storagedb_test.go
  • pkg/storage/types.go

Comment thread pkg/server/http_server.go
Comment on lines 468 to +477
if h.options.RootTLD {
for _, domain := range h.options.Domains {
interactions, _ := h.options.Storage.GetInteractionsWithId(domain)
interactions, _ := h.options.Storage.GetInteractionsWithIdForConsumer(domain, ID)
// root domains interaction are not encrypted
tlddata = append(tlddata, interactions...)
}
}
if h.options.Token != "" {
// auth token interactions are not encrypted
extradata, _ = h.options.Storage.GetInteractionsWithId(h.options.Token)
extradata, _ = h.options.Storage.GetInteractionsWithIdForConsumer(h.options.Token, ID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not silently ignore shared-data fetch errors in poll flow.

Line 470 and Line 477 ignore storage errors. That can return partial data as success and mask backend failures.

Proposed patch
 if h.options.RootTLD {
 	for _, domain := range h.options.Domains {
-		interactions, _ := h.options.Storage.GetInteractionsWithIdForConsumer(domain, ID)
+		interactions, err := h.options.Storage.GetInteractionsWithIdForConsumer(domain, ID)
+		if err != nil {
+			gologger.Warning().Msgf("Could not get root-tld interactions for %s and consumer %s: %s\n", domain, ID, err)
+			continue
+		}
 		// root domains interaction are not encrypted
 		tlddata = append(tlddata, interactions...)
 	}
 }
 if h.options.Token != "" {
 	// auth token interactions are not encrypted
-	extradata, _ = h.options.Storage.GetInteractionsWithIdForConsumer(h.options.Token, ID)
+	extradata, err = h.options.Storage.GetInteractionsWithIdForConsumer(h.options.Token, ID)
+	if err != nil {
+		gologger.Warning().Msgf("Could not get token interactions for consumer %s: %s\n", ID, err)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/http_server.go` around lines 468 - 477, The code is silently
ignoring errors from storage calls in the poll flow: update the blocks that call
h.options.Storage.GetInteractionsWithIdForConsumer (used for root TLDs and for
h.options.Token) to check the returned error instead of discarding it; if an
error is returned, propagate it (return it from the surrounding
handler/function) or at minimum log it and return an appropriate error response
so backend failures do not appear as partial success — update references to ID,
tlddata, extradata, and the h.options.RootTLD/h.options.Token branches to handle
and surface the error from GetInteractionsWithIdForConsumer.

Comment thread README.md
Comment on lines +705 to +708
```console
$ export INTERACTSH_MAX_SHARED_INTERACTIONS=50000
$ interactsh-server -domain hackwithautomation.com -wildcard
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdownlint MD014 in the new example block.

Lines 706-707 use shell prompts without output. Prefer a bash block without $ prefixes.

Proposed patch
-```console
-$ export INTERACTSH_MAX_SHARED_INTERACTIONS=50000
-$ interactsh-server -domain hackwithautomation.com -wildcard
+```bash
+export INTERACTSH_MAX_SHARED_INTERACTIONS=50000
+interactsh-server -domain hackwithautomation.com -wildcard
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>

[warning] 706-706: Dollar signs used before commands without showing output

(MD014, commands-show-output)

---

[warning] 707-707: Dollar signs used before commands without showing output

(MD014, commands-show-output)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @README.md around lines 705 - 708, Change the fenced code block from using
the "console" language with "$" prompts to a "bash" block without "$" prefixes:
remove the leading "$ " on the two lines referencing the
INTERACTSH_MAX_SHARED_INTERACTIONS environment variable and the
interactsh-server command, and update the block fence language from "console" to
"bash" so the block becomes a plain bash snippet (affecting the lines that set
INTERACTSH_MAX_SHARED_INTERACTIONS and call interactsh-server).


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/server/dns_server.go`:
- Around line 656-665: The MX branch incorrectly treats record.Priority == 0 as
"unset" and forces a default of 10, preventing explicit zero priorities; update
the record representation to distinguish unset vs explicit zero (e.g., change
CustomRecordConfig.Priority to a pointer type like *uint16 or add an
IsPrioritySet boolean) and then modify the MX handling in dns_server.go to use
that sentinel (check record.Priority != nil or IsPrioritySet) and only apply the
default 10 when the priority is truly unset, keeping Preference set to 0 when
explicitly provided; ensure references to record.Priority in the MX case and any
record creation/parsing code are updated accordingly.
- Around line 633-650: In the "A" and "AAAA" handling blocks (case "A" / case
"AAAA") the code only checks net.ParseIP but doesn't validate address family:
for "A" ensure ip.To4() != nil before appending the dns.A record and return an
error if it's nil; for "AAAA" ensure ip.To4() == nil (i.e. not an IPv4 or
IPv4-mapped address) and ip.To16() != nil before appending the dns.AAAA record
and return an error if those checks fail; update the validation around
net.ParseIP, ip.To4(), ip.To16(), and the m.Answer append/use of dns.A and
dns.AAAA accordingly.

In `@README.md`:
- Line 251: The README contains a typo in the sentence describing supported
servers: replace the misspelled word "authencaited" with "authenticated" in the
paragraph that reads "supports other self-hosted public/authencaited interactsh
servers as well" so it becomes "supports other self-hosted public/authenticated
interactsh servers as well"; update that exact phrase in README.md.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ef22914-813f-4a6c-91ae-c7eb0fe87d29

📥 Commits

Reviewing files that changed from the base of the PR and between fe9869d and 989a1c9.

⛔ Files ignored due to path filters (1)
  • cmd/interactsh-server/example-custom-records.yaml is excluded by !**/*.yaml
📒 Files selected for processing (3)
  • README.md
  • pkg/server/dns_server.go
  • pkg/server/dns_server_test.go

Comment thread pkg/server/dns_server.go
Comment on lines +633 to +650
case "A":
ip := net.ParseIP(record.Value)
if ip == nil {
return fmt.Errorf("invalid IPv4 address for A record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
A: ip.To4(),
})
case "AAAA":
ip := net.ParseIP(record.Value)
if ip == nil {
return fmt.Errorf("invalid IPv6 address for AAAA record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
AAAA: ip.To16(),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Insufficient validation for A and AAAA record IP address types.

For A records: net.ParseIP succeeds for IPv6 addresses, but ip.To4() returns nil. The check on line 635 only verifies parsing succeeded, not that it's actually IPv4. This could result in appending an A record with A: nil.

For AAAA records: If an IPv4 address is provided, To16() returns an IPv4-mapped IPv6 address (::ffff:x.x.x.x), which may not be the intended behavior.

🐛 Proposed fix to validate address family
 	case "A":
 		ip := net.ParseIP(record.Value)
-		if ip == nil {
+		ipv4 := ip.To4()
+		if ip == nil || ipv4 == nil {
 			return fmt.Errorf("invalid IPv4 address for A record: %s", record.Value)
 		}
 		m.Answer = append(m.Answer, &dns.A{
 			Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
-			A:   ip.To4(),
+			A:   ipv4,
 		})
 	case "AAAA":
 		ip := net.ParseIP(record.Value)
-		if ip == nil {
+		if ip == nil || ip.To4() != nil {
 			return fmt.Errorf("invalid IPv6 address for AAAA record: %s", record.Value)
 		}
 		m.Answer = append(m.Answer, &dns.AAAA{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "A":
ip := net.ParseIP(record.Value)
if ip == nil {
return fmt.Errorf("invalid IPv4 address for A record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
A: ip.To4(),
})
case "AAAA":
ip := net.ParseIP(record.Value)
if ip == nil {
return fmt.Errorf("invalid IPv6 address for AAAA record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
AAAA: ip.To16(),
})
case "A":
ip := net.ParseIP(record.Value)
ipv4 := ip.To4()
if ip == nil || ipv4 == nil {
return fmt.Errorf("invalid IPv4 address for A record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.A{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl},
A: ipv4,
})
case "AAAA":
ip := net.ParseIP(record.Value)
if ip == nil || ip.To4() != nil {
return fmt.Errorf("invalid IPv6 address for AAAA record: %s", record.Value)
}
m.Answer = append(m.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl},
AAAA: ip.To16(),
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/dns_server.go` around lines 633 - 650, In the "A" and "AAAA"
handling blocks (case "A" / case "AAAA") the code only checks net.ParseIP but
doesn't validate address family: for "A" ensure ip.To4() != nil before appending
the dns.A record and return an error if it's nil; for "AAAA" ensure ip.To4() ==
nil (i.e. not an IPv4 or IPv4-mapped address) and ip.To16() != nil before
appending the dns.AAAA record and return an error if those checks fail; update
the validation around net.ParseIP, ip.To4(), ip.To16(), and the m.Answer
append/use of dns.A and dns.AAAA accordingly.

Comment thread pkg/server/dns_server.go
Comment on lines +656 to +665
case "MX":
priority := record.Priority
if priority == 0 {
priority = 10 // default priority if not specified
}
m.Answer = append(m.Answer, &dns.MX{
Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: ttl},
Mx: dns.Fqdn(record.Value),
Preference: priority,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

MX priority 0 cannot be explicitly set.

Priority 0 is a valid MX priority value (highest preference), but the code treats it as "unset" and defaults to 10. Users wanting to set priority 0 explicitly cannot do so.

Consider documenting this behavior, or using a pointer (*uint16) in CustomRecordConfig.Priority to distinguish "not set" from "explicitly zero".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/server/dns_server.go` around lines 656 - 665, The MX branch incorrectly
treats record.Priority == 0 as "unset" and forces a default of 10, preventing
explicit zero priorities; update the record representation to distinguish unset
vs explicit zero (e.g., change CustomRecordConfig.Priority to a pointer type
like *uint16 or add an IsPrioritySet boolean) and then modify the MX handling in
dns_server.go to use that sentinel (check record.Priority != nil or
IsPrioritySet) and only apply the default 10 when the priority is truly unset,
keeping Preference set to 0 when explicitly provided; ensure references to
record.Priority in the MX case and any record creation/parsing code are updated
accordingly.

Comment thread README.md
## Interactsh Web Client

[Interactsh-web](https://github.com/projectdiscovery/interactsh-web) is a free and open-source web client that displays Interactsh interactions in a well-managed dashboard in your browser. It uses the browser's local storage to store and display all incoming interactions. By default, the web client is configured to use **interact.sh** as default interactsh server, and supports other self-hosted public/authencaited interactsh servers as well.
[Interactsh-web](https://github.com/projectdiscovery/interactsh-web) is a free and open-source web client that displays Interactsh interactions in a well-managed dashboard in your browser. It uses the browser's local storage to store and display all incoming interactions. By default, the web client is configured to use **oast.fun** as default interactsh server, and supports other self-hosted public/authencaited interactsh servers as well.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo: "authencaited" → "authenticated".

The word "authencaited" is misspelled and should be "authenticated".

📝 Proposed fix
-[Interactsh-web](https://github.com/projectdiscovery/interactsh-web) is a free and open-source web client that displays Interactsh interactions in a well-managed dashboard in your browser. It uses the browser's local storage to store and display all incoming interactions. By default, the web client is configured to use **oast.fun** as default interactsh server, and supports other self-hosted public/authencaited interactsh servers as well.
+[Interactsh-web](https://github.com/projectdiscovery/interactsh-web) is a free and open-source web client that displays Interactsh interactions in a well-managed dashboard in your browser. It uses the browser's local storage to store and display all incoming interactions. By default, the web client is configured to use **oast.fun** as default interactsh server, and supports other self-hosted public/authenticated interactsh servers as well.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[Interactsh-web](https://github.com/projectdiscovery/interactsh-web) is a free and open-source web client that displays Interactsh interactions in a well-managed dashboard in your browser. It uses the browser's local storage to store and display all incoming interactions. By default, the web client is configured to use **oast.fun** as default interactsh server, and supports other self-hosted public/authencaited interactsh servers as well.
[Interactsh-web](https://github.com/projectdiscovery/interactsh-web) is a free and open-source web client that displays Interactsh interactions in a well-managed dashboard in your browser. It uses the browser's local storage to store and display all incoming interactions. By default, the web client is configured to use **oast.fun** as default interactsh server, and supports other self-hosted public/authenticated interactsh servers as well.
🧰 Tools
🪛 LanguageTool

[grammar] ~251-~251: Ensure spelling is correct
Context: ..., and supports other self-hosted public/authencaited interactsh servers as well. A hosted i...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 251, The README contains a typo in the sentence describing
supported servers: replace the misspelled word "authencaited" with
"authenticated" in the paragraph that reads "supports other self-hosted
public/authencaited interactsh servers as well" so it becomes "supports other
self-hosted public/authenticated interactsh servers as well"; update that exact
phrase in README.md.

@dogancanbakir
dogancanbakir merged commit 810180a into main Mar 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

release interactsh v1.3.1

3 participants