Benchmark: grafana PR 79265 - #11
celmis-codereviewer wants to merge 5 commits into
Conversation
celmis-codereviewer
left a comment
There was a problem hiding this comment.
💬 COMMENT — findings to consider
Full findings and scope are in the review summary comment on this pull request — one persistent comment, updated in place on every run.
| device.UpdatedAt.UTC().Add(-anonymousDeviceExpiration), device.UpdatedAt.UTC().Add(time.Minute), | ||
| } | ||
| err := s.sqlStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { | ||
| args = append([]interface{}{query}, args...) |
There was a problem hiding this comment.
Why: args is declared on line 80 outside the closure and mutated on line 84 via append; if WithDbSession retries the callback, args will retain the previously prepended query string and receive a duplicate, causing dbSession.Exec on line 85 to fail.
🟠 Outer slice variable mutated inside database session closure
The args slice is declared outside WithDbSession at line 80. Inside the closure passed to WithDbSession, line 84 reassigns args = append([]interface{}{query}, args...). If WithDbSession retries the callback (e.g., due to a transient database error or connection retry), args will already contain query at index 0 from the previous execution, leading to duplicate query arguments being passed to dbSession.Exec on line 85 and causing SQL execution failure.
To fix this, avoid mutating the outer args slice inside the closure. Either construct the argument list locally within the closure or pass query directly to dbSession.Exec.
| args = append([]interface{}{query}, args...) | |
| result, err := dbSession.Exec(append([]interface{}{query}, args...)...) |
agent: defect · rule: defect.state-mutation · confidence: 0.95
| if err := a.anonDeviceService.TagDevice(newCtx, httpReqCopy, anonymous.AnonDeviceUI); err != nil { | ||
| a.log.Warn("Failed to tag anonymous session", "error", err) | ||
| if err := a.anonDeviceService.TagDevice(ctx, httpReqCopy, anonymous.AnonDeviceUI); err != nil { | ||
| if errors.Is(err, anonstore.ErrDeviceLimitReached) { |
There was a problem hiding this comment.
Why: err can be ErrDeviceLimitReached on line 45; returning nil, err blocks anonymous user authentication when the device limit is reached, allowing an unauthenticated attacker to trigger a denial of service for all anonymous users.
🟠 Anonymous authentication failure on device limit causes Denial of Service
When anonymousDeviceLimit is configured, reaching the device limit causes TagDevice to return ErrDeviceLimitReached. Checking for this error and returning nil, err inside Authenticate fails authentication for anonymous requests. An unauthenticated attacker can make requests with different device headers or cookies until anonymousDeviceLimit is reached, preventing any subsequent anonymous user from accessing Grafana.
| if errors.Is(err, anonstore.ErrDeviceLimitReached) { | |
| if errors.Is(err, anonstore.ErrDeviceLimitReached) { | |
| a.log.Debug("Anonymous device limit reached", "error", err) | |
| } |
agent: security · rule: sec.cwe-400 · confidence: 0.95
| var query string | ||
|
|
||
| // if device limit is reached, only update devices | ||
| if s.deviceLimit > 0 { |
There was a problem hiding this comment.
Why: On line 109, CountDevices reads the current device count outside a lock or transaction before line 118 inserts a new record, allowing concurrent requests to both read a count below s.deviceLimit and create devices that exceed the limit.
🟡 Check-then-act race condition in device limit enforcement
When s.deviceLimit is set, CreateOrUpdateDevice counts active devices on line 109 and checks whether count >= s.deviceLimit on line 113. Because this read and the subsequent insertion on line 118 are not atomic or guarded by a transaction lock, concurrent anonymous authentication requests can both observe count < s.deviceLimit at the same time and create new devices, exceeding the configured limit.
| if s.deviceLimit > 0 { | |
| Perform the device count check and device insertion within an isolated database transaction or under a lock to prevent concurrent requests from exceeding `deviceLimit`. |
agent: defect · rule: defect.race-condition · confidence: 0.95
🤖 Code Review for PR #11⚙ ADJUSTED — graph context partial (9 of 11 changed files): 2 of 11 changed files have no symbols in the index; 1 of them is still in the checkout the index was built from (packages/grafana-data/src/types/config.ts) — the index is stale there, or the extractor could not parse it; run 💬 COMMENT — findings to consider Findings
Scope
Performance
Powered by Code Analyzer · context: tree-sitter graph + structural, cve, contract, security, defect |
Benchmark reproduction of grafana#79265