Skip to content

fix(docker): avoid panic on colliding nested crowdsec labels#4555

Open
arpitjain099 wants to merge 3 commits into
crowdsecurity:masterfrom
arpitjain099:fix/docker-label-nested-panic
Open

fix(docker): avoid panic on colliding nested crowdsec labels#4555
arpitjain099 wants to merge 3 commits into
crowdsecurity:masterfrom
arpitjain099:fix/docker-label-nested-panic

Conversation

@arpitjain099

Copy link
Copy Markdown

parseKeyToMap walks into nested label maps with an unchecked type assertion:

for i := 1; i < len(parts)-1; i++ {
    if _, ok := m[parts[i]]; !ok {
        m[parts[i]] = make(map[string]interface{})
    }
    m = m[parts[i]].(map[string]interface{})
}

If a container carries both a leaf label and a branch label under the same key, the intermediate key already holds a string, so the assertion panics:

crowdsec.enable=true
crowdsec.enable.foo=bar
-> interface conversion: interface {} is string, not map[string]interface {}

Container labels are set by whoever launches the container, so on a shared host this can crash the acquisition goroutine (an unrecovered goroutine panic takes down the process). The trigger is also order-dependent because parseLabels ranges over a Go map, so today it is a flaky panic vs. a silent clobber depending on iteration order.

Fix: use the comma-ok form and replace a non-map (or absent) value with a fresh nested map, so a deeper key takes precedence instead of panicking.

Added TestParseLabelsNestedCollisionDoesNotPanic (asserts no panic and that unrelated labels survive; the exact winner is map-order-dependent, which is pre-existing). Ran go test ./pkg/acquisition/modules/docker/ locally (passes; the test panics without the fix).

parseKeyToMap descended into nested label maps with an unchecked type
assertion:

    m = m[parts[i]].(map[string]interface{})

When a container carries both a leaf label and a branch label under the
same key (for example crowdsec.enable=true and crowdsec.enable.foo=bar),
the intermediate key already holds a string, so the assertion panics
with "interface conversion: interface {} is string, not
map[string]interface {}". Container labels are set by whoever launches
the container, so on a shared host this can crash the acquisition
goroutine.

Use the comma-ok form and replace a non-map (or absent) value with a
fresh nested map. Added a regression test.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

@arpitjain099: There are no 'kind' label on this PR. You need a 'kind' label to generate the release automatically.

  • /kind feature
  • /kind enhancement
  • /kind refactoring
  • /kind fix
  • /kind chore
  • /kind dependencies
Details

I am a bot created to help the crowdsecurity developers manage community feedback and contributions. You can check out my manifest file to understand my behavior and what I can do. If you want to use this for your project, you can check out the BirthdayResearch/oss-governance-bot repository.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

@arpitjain099: There are no area labels on this PR. You can add as many areas as you see fit.

  • /area agent
  • /area local-api
  • /area cscli
  • /area appsec
  • /area security
  • /area configuration
Details

I am a bot created to help the crowdsecurity developers manage community feedback and contributions. You can check out my manifest file to understand my behavior and what I can do. If you want to use this for your project, you can check out the BirthdayResearch/oss-governance-bot repository.

@blotus blotus added this to the Next release milestone Jul 10, 2026
// The key is absent, or a leaf value (e.g. a sibling label like
// crowdsec.enable=true) is already stored here. Replace it with a
// nested map instead of panicking on the type assertion.
next = make(map[string]interface{})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think there's a chance of skipping some values depending on the order of the elements in the map (which is random) ?

We probably want iterate over a sorted slice of all labels to avoid potential overwrite.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch, you're right that the random map order made this non-deterministic. I pushed a change to parseLabels that sorts the keys before building the tree, so the shorter key (crowdsec.enable) is always applied before the more specific one (crowdsec.enable.foo).

With genuinely colliding labels only one can survive, since enable can't be both a string and a nested map at once, but now the branch wins on every run instead of it being a coin flip on iteration order. I strengthened the test to assert the branch wins and to run parseLabels repeatedly so any remaining order dependence would fail it.

arpitjain099 and others added 2 commits July 10, 2026 20:18
Colliding nested labels (a leaf and a branch under the same key, e.g.
crowdsec.enable and crowdsec.enable.foo) can only resolve one way, but
Go map iteration order is random, so which value won was decided at
random from run to run. Iterate over sorted keys so the shorter, less
specific key is always applied first and then overwritten by the more
specific one. Strengthen the test to assert the branch wins
deterministically and unrelated labels survive.

Signed-off-by: arpitjain099 <arpitjain099@gmail.com>
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.81%. Comparing base (62844fa) to head (b46ed14).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4555      +/-   ##
==========================================
- Coverage   63.81%   63.81%   -0.01%     
==========================================
  Files         507      507              
  Lines       38584    38590       +6     
==========================================
+ Hits        24624    24625       +1     
- Misses      11613    11617       +4     
- Partials     2347     2348       +1     
Flag Coverage Δ
bats 41.68% <0.00%> (-0.02%) ⬇️
unit-linux 40.05% <100.00%> (-0.07%) ⬇️
unit-windows 28.63% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants