Skip to content

Conversation

@mariocandela
Copy link
Owner

@mariocandela mariocandela commented Nov 15, 2025

All Submissions:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?

New Feature Submissions:

  1. Does your submission pass tests?
  2. Have you lint your code locally before submission?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully run tests with your changes locally?

Summary by CodeRabbit

  • New Features

    • Added automatic configuration change detection with continuous periodic monitoring to identify and process configuration updates
    • Enhanced event data handling with improved serialization and structured event information processing
  • Tests

    • Added comprehensive test coverage for configuration change detection and event data transformation
  • Chores

    • Updated Docker Compose configuration

@coderabbitai
Copy link

coderabbitai bot commented Nov 15, 2025

Walkthrough

A new configuration change verification feature is introduced with periodic polling to detect modifications in Beelzebub cloud configurations. API signatures are updated across multiple components to support hash-based change detection and background monitoring, with optional enablement via boolean flags.

Changes

Cohort / File(s) Summary
Builder API Updates
builder/builder.go, builder/director.go
Updated InitBeelzebubCloud calls to pass new boolean parameters (true and false respectively); adapted GetHoneypotsConfigurations to unpack additional hash return value
Core Plugin Implementation
plugins/beelzebub-cloud.go
Added EventDTO type; expanded InitBeelzebubCloud signature with enableVerifyConfigurationsChanged parameter; expanded GetHoneypotsConfigurations to return configuration hash; added verifyConfigurationsChanged() background goroutine for periodic polling; added mapToEventDTO() conversion logic; introduced PollingInterval field and pluggable exitFunction
Plugin Tests
plugins/beelzebub-cloud_test.go
Propagated new InitBeelzebubCloud boolean parameter across all tests; updated GetHoneypotsConfigurations assertions to handle hash return value; added TestVerifyConfigurationsChanged and TestMapToEventDTO
Configuration Hashing
parser/configurations_parser.go, parser/configurations_parser_test.go
Added HashCode() method to BeelzebubServiceConfiguration computing SHA-256 hash of JSON serialization; added corresponding test coverage
Docker Compose
docker-compose.yml
Removed top-level version declaration

Sequence Diagram

sequenceDiagram
    participant Init as Initialization
    participant Cloud as beelzebubCloud
    participant Verifier as Verification Loop
    participant API as Beelzebub API
    participant Process as Process Exit

    Init->>Cloud: InitBeelzebubCloud(uri, token, enableVerify=true)
    Cloud->>Cloud: Set PollingInterval (15s)
    alt enableVerify is true
        Cloud->>Verifier: Start background goroutine
    end
    
    loop Every PollingInterval
        Verifier->>Cloud: verifyConfigurationsChanged()
        Cloud->>API: GetHoneypotsConfigurations()
        API-->>Cloud: configs + hash
        Cloud->>Cloud: Compute current hash
        alt Hash changed
            Cloud->>Process: exitFunction(1)
            Process-->>Process: Exit process
        else Hash unchanged
            Note over Cloud: Continue polling
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring attention:
    • plugins/beelzebub-cloud.go: New background goroutine in verifyConfigurationsChanged() with concurrency and exit handling; review timing logic and goroutine lifecycle
    • Hash computation logic across parser/configurations_parser.go and plugins/beelzebub-cloud.go; verify SHA-256 hash consistency
    • API signature changes impact multiple call sites; ensure all callers properly handle new return values and parameters
    • EventDTO mapping logic in mapToEventDTO() for correctness of event field translation

Suggested labels

enhancement

Poem

🐰 A hashing hare hops through the code,
Watching configs on the polling road,
When changes come, it exits fast,
Configuration checks have come at last!
Beelzebub now sleeps more sound—
Configuration shifts are quickly found! 🔍

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only the unchecked template boilerplate without any meaningful explanation of the actual changes, features added, or implementation details. Fill in the template with specific explanations of: what features were added (configuration hashing, polling for changes, event mapping), why they were included, which tests were added, and confirmation that tests pass locally.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Feat: Improve cloud plugin' is vague and generic, using non-descriptive language that doesn't convey the specific changes made (API signature updates, configuration hashing, background polling, event DTO mapping). Use a more specific title that highlights the main feature, such as 'Feat: Add configuration change detection and event DTO mapping to cloud plugin' or similar.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch beelzebub-improve-cloud-plugin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov
Copy link

codecov bot commented Nov 15, 2025

Codecov Report

❌ Patch coverage is 71.91011% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.55%. Comparing base (e168bb4) to head (d16be0b).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
plugins/beelzebub-cloud.go 73.17% 16 Missing and 6 partials ⚠️
parser/configurations_parser.go 57.14% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #240      +/-   ##
==========================================
- Coverage   83.33%   81.55%   -1.78%     
==========================================
  Files           7        7              
  Lines         546      618      +72     
==========================================
+ Hits          455      504      +49     
- Misses         77       93      +16     
- Partials       14       21       +7     

☔ View full report in Codecov by Sentry.
📢 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.

@mariocandela mariocandela merged commit 6435971 into main Nov 15, 2025
3 of 6 checks passed
@mariocandela mariocandela deleted the beelzebub-improve-cloud-plugin branch November 15, 2025 16:00
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
parser/configurations_parser.go (1)

4-7: HashCode implementation looks correct; consider documenting its scope for future fields

The SHA‑256 over the JSON‑marshalled BeelzebubServiceConfiguration is straightforward and deterministic with the current struct (no maps, and runtime fields like Command.Regex effectively serialize as {} only). This is fine for now.

However, any future JSON‑serializable, non‑config/runtime fields added to BeelzebubServiceConfiguration will change the hash (and thus trigger “config changed”) even if the YAML config is logically the same. A brief doc comment on HashCode or on the struct to treat non-config fields as json:"-" (similar to yaml:"-") would make this more future‑proof.

Also applies to: 86-93

parser/configurations_parser_test.go (1)

185-198: HashCode test is strong but quite brittle to future changes

The test correctly exercises the full path and validates that HashCode returns a deterministic value for the mocked config. However, asserting a specific digest string means:

  • Any benign schema change (adding a field, renaming, etc.) will require updating this magic value.
  • The test couples tightly to the exact JSON representation.

You might instead assert properties such as “no error, non‑empty hash, and the hash changes when a config field is changed”, which still protects behavior while being less fragile.

builder/director.go (1)

61-73: Disabling config-change polling in the per-event strategy is appropriate

Passing false into InitBeelzebubCloud here ensures the background configuration polling goroutine is not spawned for every traced event, which would be wasteful. This keeps the send‑only strategy lightweight; if you ever see this path become hot, caching a single beelzebubCloud instance instead of constructing one per event would be a natural follow‑up.

plugins/beelzebub-cloud_test.go (1)

298-350: MapToEventDTO test is good; consider adding a headers-focused case

The test thoroughly checks that core fields are copied 1:1 from tracer.Event to EventDTO, which is useful coverage.

Given the new header handling in mapToEventDTO, it would be worth adding a small test that exercises an event with non-empty Headers (and possibly HeadersMap) so regressions in header serialization are caught by the suite.

plugins/beelzebub-cloud.go (1)

107-155: Config hashing in GetHoneypotsConfigurations is reasonable; minor cleanup and design note

The new (configs, hash, error) signature and implementation are clear:

  • YAML is unmarshalled into parser.BeelzebubServiceConfiguration.
  • Regexes are compiled with a helpful error message on failure.
  • HashCode() is computed per config and concatenated into a single hash string for change detection.

Two minor suggestions:

  • Inside the loop, var honeypotsConfig parser.BeelzebubServiceConfiguration shadows the outer []HoneypotConfigResponseDTO slice of the same name, which hurts readability. Renaming the inner variable (e.g. to serviceCfg) would make the code clearer.
  • Concatenating per-config hashes makes the overall hash sensitive to the order returned by the API. If the API’s array order is not guaranteed but you still care only about set equality, you might want to sort the per-config hashes before concatenation. If “order change == config change” is intentional, the current logic is fine.

Otherwise, the error propagation and return shape look good.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e168bb4 and d16be0b.

📒 Files selected for processing (7)
  • builder/builder.go (2 hunks)
  • builder/director.go (1 hunks)
  • docker-compose.yml (0 hunks)
  • parser/configurations_parser.go (2 hunks)
  • parser/configurations_parser_test.go (1 hunks)
  • plugins/beelzebub-cloud.go (5 hunks)
  • plugins/beelzebub-cloud_test.go (9 hunks)
💤 Files with no reviewable changes (1)
  • docker-compose.yml
🧰 Additional context used
🧬 Code graph analysis (5)
parser/configurations_parser_test.go (1)
parser/configurations_parser.go (1)
  • Init (132-139)
builder/director.go (1)
plugins/beelzebub-cloud.go (1)
  • InitBeelzebubCloud (59-74)
plugins/beelzebub-cloud_test.go (2)
plugins/beelzebub-cloud.go (3)
  • InitBeelzebubCloud (59-74)
  • HoneypotConfigResponseDTO (52-57)
  • EventDTO (18-43)
tracer/tracer.go (3)
  • Event (15-41)
  • Protocol (44-44)
  • Status (45-45)
builder/builder.go (1)
plugins/beelzebub-cloud.go (1)
  • InitBeelzebubCloud (59-74)
plugins/beelzebub-cloud.go (2)
tracer/tracer.go (3)
  • Protocol (44-44)
  • Status (45-45)
  • Event (15-41)
parser/configurations_parser.go (2)
  • Command (96-104)
  • BeelzebubServiceConfiguration (68-84)
🔇 Additional comments (1)
plugins/beelzebub-cloud_test.go (1)

17-52: Tests correctly reflect the new InitBeelzebubCloud/GetHoneypotsConfigurations APIs

All the updated tests now:

  • Pass the new boolean flag into InitBeelzebubCloud (using false to avoid spawning the polling goroutine in unit tests).
  • Correctly handle the extra configurationsHash return value from GetHoneypotsConfigurations, including asserting a deterministic hash where desired.

This keeps the suite aligned with the new plugin API and exercises both success and error paths appropriately.

Also applies to: 78-134, 136-235

Comment on lines +10 to +11
"github.com/mariocandela/beelzebub/v3/protocols/strategies/MCP"

Copy link

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

New MCP strategy wiring and cloud init look good; double-check process-exit semantics from polling

The MCP strategy import and "mcp" protocol wiring are consistent with the existing pattern, and handling the extra return from GetHoneypotsConfigurations by ignoring the hash here is fine.

Enabling verification via InitBeelzebubCloud(conf.URI, conf.AuthToken, true) means Run() now starts a background goroutine that may terminate the whole process on:

  • Any error in verifyConfigurationsChanged (via log.Fatalf), or
  • A configuration change (via exitFunction, defaulting to os.Exit).

If that “exit on transient API failure / config change” behavior is intentional, this is fine; otherwise, consider softening it (e.g., log and retry with backoff, or invoke a reload callback) rather than exiting the process from within a background goroutine.

Also applies to: 114-135


Comment on lines +237 to +296
func TestVerifyConfigurationsChanged(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()

uri := "localhost:8081"

callCount := 0

httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
callCount++

config := "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-4o\"\n openAISecretKey: \"1234\"\n"

if callCount > 1 {
config = "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT MODIFIED\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-3.5-turbo\"\n openAISecretKey: \"1234\"\n"
}

resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
{
ID: "123456",
Config: config,
TokenID: "1234567",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)

var exitInvoked bool = false
exitCalled := make(chan bool)

exitFunction = func(c int) {
exitInvoked = true
exitCalled <- true
}

beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk", false)
beelzebubCloud.client = client
beelzebubCloud.PollingInterval = 100 * time.Millisecond

go func() {
err := beelzebubCloud.verifyConfigurationsChanged()
if err != nil {
t.Errorf("verifyConfigurationsChanged returned with error: %v", err)
}
}()

select {
case <-exitCalled:
assert.True(t, exitInvoked, "exitFunction should have been invoked")
assert.Greater(t, callCount, 1, "Should have made at least 2 API calls")
case <-time.After(2 * time.Second):
t.Fatal("Test timed out waiting for exitFunction to be called")
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix data races in TestVerifyConfigurationsChanged (exitInvoked and callCount)

exitInvoked and callCount are written inside the HTTP responder / verification goroutine and read on the main test goroutine, which is a data race under go test -race.

You can avoid sharing mutable state by sending the call count through the existing channel and dropping exitInvoked entirely. For example:

-	callCount := 0
+	callCount := 0
@@
-	var exitInvoked bool = false
-	exitCalled := make(chan bool)
-
-	exitFunction = func(c int) {
-		exitInvoked = true
-		exitCalled <- true
-	}
+	exitCalled := make(chan int, 1)
+
+	exitFunction = func(c int) {
+		// Capture the current callCount when a change is detected.
+		exitCalled <- callCount
+	}
@@
-	case <-exitCalled:
-		assert.True(t, exitInvoked, "exitFunction should have been invoked")
-		assert.Greater(t, callCount, 1, "Should have made at least 2 API calls")
+	case count := <-exitCalled:
+		assert.Greater(t, count, 1, "Should have made at least 2 API calls")

This preserves the assertions (including “at least 2 API calls”) while eliminating racy shared variables.

📝 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
func TestVerifyConfigurationsChanged(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
callCount := 0
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
callCount++
config := "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-4o\"\n openAISecretKey: \"1234\"\n"
if callCount > 1 {
config = "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT MODIFIED\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-3.5-turbo\"\n openAISecretKey: \"1234\"\n"
}
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
{
ID: "123456",
Config: config,
TokenID: "1234567",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
var exitInvoked bool = false
exitCalled := make(chan bool)
exitFunction = func(c int) {
exitInvoked = true
exitCalled <- true
}
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk", false)
beelzebubCloud.client = client
beelzebubCloud.PollingInterval = 100 * time.Millisecond
go func() {
err := beelzebubCloud.verifyConfigurationsChanged()
if err != nil {
t.Errorf("verifyConfigurationsChanged returned with error: %v", err)
}
}()
select {
case <-exitCalled:
assert.True(t, exitInvoked, "exitFunction should have been invoked")
assert.Greater(t, callCount, 1, "Should have made at least 2 API calls")
case <-time.After(2 * time.Second):
t.Fatal("Test timed out waiting for exitFunction to be called")
}
}
func TestVerifyConfigurationsChanged(t *testing.T) {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
uri := "localhost:8081"
callCount := 0
httpmock.RegisterResponder("GET", fmt.Sprintf("%s/honeypots", uri),
func(req *http.Request) (*http.Response, error) {
callCount++
config := "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-4o\"\n openAISecretKey: \"1234\"\n"
if callCount > 1 {
config = "apiVersion: \"v1\"\nprotocol: \"ssh\"\naddress: \":2222\"\ndescription: \"SSH interactive ChatGPT MODIFIED\"\ncommands:\n - regex: \"^(.+)$\"\n plugin: \"LLMHoneypot\"\nserverVersion: \"OpenSSH\"\nserverName: \"ubuntu\"\npasswordRegex: \"^(root|qwerty|Smoker666|123456|jenkins|minecraft|sinus|alex|postgres|Ly123456)$\"\ndeadlineTimeoutSeconds: 60\nplugin:\n llmModel: \"gpt-3.5-turbo\"\n openAISecretKey: \"1234\"\n"
}
resp, err := httpmock.NewJsonResponse(200, &[]HoneypotConfigResponseDTO{
{
ID: "123456",
Config: config,
TokenID: "1234567",
},
})
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
exitCalled := make(chan int, 1)
exitFunction = func(c int) {
// Capture the current callCount when a change is detected.
exitCalled <- callCount
}
beelzebubCloud := InitBeelzebubCloud(uri, "sdjdnklfjndslkjanfk", false)
beelzebubCloud.client = client
beelzebubCloud.PollingInterval = 100 * time.Millisecond
go func() {
err := beelzebubCloud.verifyConfigurationsChanged()
if err != nil {
t.Errorf("verifyConfigurationsChanged returned with error: %v", err)
}
}()
select {
case count := <-exitCalled:
assert.Greater(t, count, 1, "Should have made at least 2 API calls")
case <-time.After(2 * time.Second):
t.Fatal("Test timed out waiting for exitFunction to be called")
}
}
🤖 Prompt for AI Agents
In plugins/beelzebub-cloud_test.go around lines 237 to 296,
TestVerifyConfigurationsChanged has data races because exitInvoked and callCount
are mutated in the HTTP responder goroutine and read on the main test goroutine;
remove the shared mutable state by dropping exitInvoked and instead send the
call count over a channel when you want to signal test completion (reuse or add
a channel like exitCalled to carry the int), increment a local callCount inside
the responder and send that value on the channel when the responder triggers the
exit path, update exitFunction to only signal via the channel (or have the
responder send after calling it), and then assert on the received call count in
the main goroutine — this eliminates the race while preserving the “at least 2
API calls” and exit invocation assertions.

Comment on lines +18 to +43
type EventDTO struct {
DateTime string
RemoteAddr string
Protocol string
Command string
CommandOutput string
Status string
Msg string
ID string
Environ string
User string
Password string
Client string
Headers string
HeadersMap map[string][]string
Cookies string
UserAgent string
HostHTTPRequest string
Body string
HTTPMethod string
RequestURI string
Description string
SourceIp string
SourcePort string
TLSServerName string
}
Copy link

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

EventDTO/mapping are mostly straightforward; header handling may be unintentionally changed

Introducing EventDTO and mapping via mapToEventDTO cleanly decouples the wire format from tracer.Event, and most fields are copied 1:1, which preserves existing behavior.

Two points to double-check:

  • Headers is now set as json.Marshal(event.Headers) when event.Headers is non-empty. Since Headers is already a string, this effectively wraps it in JSON quotes (e.g. Header: value"\"Header: value\""). Unless the consumer explicitly expects a JSON-encoded string literal, you probably want either:
    • eventDTO.Headers = event.Headers, or
    • JSON-marshalling event.HeadersMap instead and leaving Headers as-is.
  • HeadersMap exists on EventDTO but is never populated in mapToEventDTO. If the remote side relies on that field, this will silently drop information; if not, consider removing it from EventDTO to avoid confusion.

Also, the error path in mapToEventDTO (failing to marshal a string) is effectively unreachable; you can keep it for completeness, but it’s unlikely to ever trigger.

Also applies to: 76-105, 178-213



---

🏁 Script executed:

```shell
#!/bin/bash
# Inspect the EventDTO struct and related code sections
cat plugins/beelzebub-cloud.go | head -250 | tail -250

Length of output: 5767


🏁 Script executed:

#!/bin/bash
# Find and display the mapToEventDTO function
rg -n "mapToEventDTO" plugins/beelzebub-cloud.go -A 30

Length of output: 2052


🏁 Script executed:

#!/bin/bash
# Find GetHoneypotsConfigurations implementation
rg -n "GetHoneypotsConfigurations" plugins/beelzebub-cloud.go -A 20

Length of output: 1516


🏁 Script executed:

#!/bin/bash
# Find verifyConfigurationsChanged function
rg -n "verifyConfigurationsChanged" plugins/beelzebub-cloud.go -A 25

Length of output: 1662



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for HeadersMap usage across the codebase
rg -n "HeadersMap" --type go

Length of output: 196


🏁 Script executed:

#!/bin/bash
# Check tracer.Event definition
rg -n "type Event struct" -A 30 --type go

Length of output: 1290


🏁 Script executed:

#!/bin/bash
# Check if Headers field in tracer.Event and how it's set
rg -n "Headers" plugins/beelzebub-cloud.go --type go -B 2 -A 2

Length of output: 438


Fix header handling: stop double-encoding Headers and populate HeadersMap

The review findings are confirmed. The mapToEventDTO function introduces two issues:

  1. Headers is double-encoded (line 205): json.Marshal(event.Headers) treats the already-string event.Headers as a value to encode, wrapping it in JSON quotes. This transforms "Header: value" into "\"Header: value\"", changing the wire format unexpectedly.

  2. HeadersMap is silently dropped (line 178-213): The event.HeadersMap field is populated by the HTTP protocol handler (see protocols/strategies/HTTP/http.go:134) but is never copied to eventDTO.HeadersMap. If the remote consumer expects structured headers in this field, information is lost.

Fixes required (lines 204-209):

  • Replace json.Marshal(event.Headers) with direct assignment: eventDTO.Headers = event.Headers
  • Add: eventDTO.HeadersMap = event.HeadersMap (or conditionally if nil-checking is preferred)

If the remote API contract specifies JSON-encoded headers, adjust the approach accordingly, but do not silently lose the HeadersMap data.

🤖 Prompt for AI Agents
In plugins/beelzebub-cloud.go around lines 18 to 43, the EventDTO struct
handling in mapToEventDTO double-encodes the Headers field and omits copying
HeadersMap: change the mapping so you assign event.Headers directly to
eventDTO.Headers (no json.Marshal) and also set eventDTO.HeadersMap =
event.HeadersMap (or conditionally copy when non-nil) so structured headers are
preserved; ensure any nil checks as needed but do not JSON-encode the
already-string Headers.

Comment on lines +59 to 74
func InitBeelzebubCloud(uri, authToken string, enableVerifyConfigurationsChanged bool) *beelzebubCloud {
beelzebubCloud := &beelzebubCloud{
URI: uri,
AuthToken: authToken,
client: resty.New(),
PollingInterval: 15 * time.Second,
}
if enableVerifyConfigurationsChanged {
go func() {
if err := beelzebubCloud.verifyConfigurationsChanged(); err != nil {
log.Fatalf("Error verify configurations changed: %s", err.Error())
}
}()
}
return beelzebubCloud
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Background config polling is useful but currently exits the process on any error/change

InitBeelzebubCloud now optionally starts a goroutine that loops in verifyConfigurationsChanged, which:

  • Calls GetHoneypotsConfigurations() on every interval.
  • Returns on any error, causing the goroutine to invoke log.Fatalf(...) (process exit with code 1).
  • Calls exitFunction(0) (default os.Exit(0)) when a hash change is detected, also terminating the process.

This is a strong behavior change: transient network/API issues or configuration updates now kill the entire process from a background goroutine.

Consider softening this behavior:

  • Log errors and continue with backoff instead of log.Fatalf, so brief outages don’t crash the honeypot.
  • After calling exitFunction, either return or break to make the control flow explicit (especially important when exitFunction is overridden in tests or future code).
  • Optionally accept a context or a stop channel so the polling goroutine can be shut down cleanly if the plugin is ever reused in a shorter-lived context.

These tweaks would keep the verification feature while improving robustness and testability.

Also applies to: 157-176

🤖 Prompt for AI Agents
In plugins/beelzebub-cloud.go around lines 59-74 (and similarly 157-176), the
background polling goroutine currently calls log.Fatalf on any error and invokes
exitFunction(0) on hash changes which unconditionally terminates the process;
change this to log the error and retry with a backoff (do not call log.Fatalf),
make the control flow explicit after calling exitFunction by returning or
breaking so tests/overrides behave deterministically, and add an optional
context.Context or stop channel parameter to InitBeelzebubCloud so the goroutine
can be cancelled cleanly; implement backoff/retry on transient
GetHoneypotsConfigurations errors and ensure errors are logged with details
rather than exiting the process.

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.

2 participants