fix(backend): back off Sentinel lockdown retries, validate product_id in /proxy #39

Merged
starkSV merged 1 commit from fix/sentinel-lockdown-and-proxy-validation into main 2026-07-31 11:49:59 +05:30
starkSV commented 2026-07-31 11:36:28 +05:30 (Migrated from github.com)

Summary

  • Bumps `lockdownTTL` from 90 minutes to 5 hours. Log analysis over 17 days showed 181 Sentinel-block retry attempts on `/proxy`, all 181 failed. Retrying that often yields zero value and just adds more blocked-request noise against our own IP for nothing in return.
  • Adds catalog validation to `/proxy` (reusing the existing `validContributeProducts` allow-list) so an unknown `product_id` gets rejected with `404` before burning an outbound Microsoft session attempt and a Sentinel-block hit. Found via a stray `product_id=2861` (never a real product) in the backend logs.
  • Updated the two Sentinel-lockdown log lines that had "90min" hardcoded in the message to use the actual `lockdownTTL` value instead, so they stay accurate if this gets tuned again.

Test plan

  • `go build ./...` and `go vet ./...` pass
  • Verified locally: `product_id=2861` (unknown) -> `404`, no MS fetch attempted (confirmed via logs); missing params -> `400` unchanged; `product_id=3262` (valid) -> proceeds to a real MS session attempt as before
  • After merge: redeploy backend (Coolify) for the new lockdown TTL and validation to take effect live

Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com

Summary by CodeRabbit

  • Bug Fixes
    • Requests for products outside the contribution allow-list now return a 404 response before processing.
    • Sentinel negative-cache lockdowns now remain active for up to 5 hours.
    • Lockdown logs now display the configured duration for improved clarity.
## Summary - Bumps \`lockdownTTL\` from 90 minutes to 5 hours. Log analysis over 17 days showed 181 Sentinel-block retry attempts on \`/proxy\`, all 181 failed. Retrying that often yields zero value and just adds more blocked-request noise against our own IP for nothing in return. - Adds catalog validation to \`/proxy\` (reusing the existing \`validContributeProducts\` allow-list) so an unknown \`product_id\` gets rejected with \`404\` before burning an outbound Microsoft session attempt and a Sentinel-block hit. Found via a stray \`product_id=2861\` (never a real product) in the backend logs. - Updated the two Sentinel-lockdown log lines that had "90min" hardcoded in the message to use the actual \`lockdownTTL\` value instead, so they stay accurate if this gets tuned again. ## Test plan - [x] \`go build ./...\` and \`go vet ./...\` pass - [x] Verified locally: \`product_id=2861\` (unknown) -> \`404\`, no MS fetch attempted (confirmed via logs); missing params -> \`400\` unchanged; \`product_id=3262\` (valid) -> proceeds to a real MS session attempt as before - [ ] After merge: redeploy backend (Coolify) for the new lockdown TTL and validation to take effect live Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Requests for products outside the contribution allow-list now return a 404 response before processing. * Sentinel negative-cache lockdowns now remain active for up to 5 hours. * Lockdown logs now display the configured duration for improved clarity. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
cloudflare-workers-and-pages[bot] commented 2026-07-31 11:36:35 +05:30 (Migrated from github.com)

Deploying msdl-frontend with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6ab849b
Status: ✅  Deploy successful!
Preview URL: https://c3a8ca75.msdl-frontend.pages.dev
Branch Preview URL: https://fix-sentinel-lockdown-and-pr.msdl-frontend.pages.dev

View logs

## Deploying msdl-frontend with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages <table><tr><td><strong>Latest commit:</strong> </td><td> <code>6ab849b</code> </td></tr> <tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr> <tr><td><strong>Preview URL:</strong></td><td> <a href='https://c3a8ca75.msdl-frontend.pages.dev'>https://c3a8ca75.msdl-frontend.pages.dev</a> </td></tr> <tr><td><strong>Branch Preview URL:</strong></td><td> <a href='https://fix-sentinel-lockdown-and-pr.msdl-frontend.pages.dev'>https://fix-sentinel-lockdown-and-pr.msdl-frontend.pages.dev</a> </td></tr> </table> [View logs](https://dash.cloudflare.com/?to=/630cc520524402c8c1de35025c15b122/pages/view/msdl-frontend/c3a8ca75-f184-4fcc-8ad3-081b6581800a)
coderabbitai[bot] commented 2026-07-31 11:36:43 +05:30 (Migrated from github.com)

Review Change Stack

📝 Walkthrough

Walkthrough

The change increases the Sentinel negative-cache lockdown to five hours, updates related duration logs, and makes /proxy return HTTP 404 for product IDs outside the contribution allow-list.

Changes

Proxy and Sentinel behavior

Layer / File(s) Summary
Sentinel lockdown duration and logging
backend/main.go
lockdownTTL now uses five hours. Sentinel stale and no-stale responses log the configured duration.
Proxy product validation
backend/main.go
/proxy returns HTTP 404 when productID is not in validContributeProducts.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes both main changes: increased Sentinel lockdown backoff and product_id validation in /proxy.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sentinel-lockdown-and-proxy-validation

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/starkSV/windows-iso-downloader/pull/39?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- walkthrough_start --> <details> <summary>📝 Walkthrough</summary> ## Walkthrough The change increases the Sentinel negative-cache lockdown to five hours, updates related duration logs, and makes `/proxy` return HTTP 404 for product IDs outside the contribution allow-list. ### Changes **Proxy and Sentinel behavior** |Layer / File(s)|Summary| |---|---| |**Sentinel lockdown duration and logging** <br> `backend/main.go`|`lockdownTTL` now uses five hours. Sentinel stale and no-stale responses log the configured duration.| |**Proxy product validation** <br> `backend/main.go`|`/proxy` returns HTTP 404 when `productID` is not in `validContributeProducts`.| **Estimated code review effort:** 2 (Simple) | ~10 minutes </details> <!-- walkthrough_end --> <!-- pre_merge_checks_walkthrough_start --> <details> <summary>🚥 Pre-merge checks | ✅ 5</summary> <details> <summary>✅ Passed checks (5 passed)</summary> | Check name | Status | Explanation | | :------------------------: | :------- | :----------------------------------------------------------------------------------------------------------------------- | | Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. | | Title check | ✅ Passed | The title clearly summarizes both main changes: increased Sentinel lockdown backoff and product_id validation in /proxy. | | Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. | | Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. | | Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. | </details> </details> <!-- pre_merge_checks_walkthrough_end --> <!-- finishing_touch_checkbox_start --> <details> <summary>✨ Finishing Touches</summary> <details> <summary>📝 Generate docstrings</summary> - [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create stacked PR - [ ] <!-- {"checkboxId": "3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit on current branch </details> <details> <summary>🧪 Generate unit tests (beta)</summary> - [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests - [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Commit unit tests in branch `fix/sentinel-lockdown-and-proxy-validation` </details> </details> <!-- finishing_touch_checkbox_end --> <!-- This is an auto-generated comment: all tool run failures by coderabbit.ai --> > [!WARNING] > There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. > > <details> > <summary>🔧 golangci-lint (2.12.2)</summary> > > level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" > > > > > </details> <!-- end of auto-generated comment: all tool run failures by coderabbit.ai --> <!-- tips_start --> --- Thanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=starkSV/windows-iso-downloader&utm_content=39)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. <details> <summary>❤️ Share</summary> - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code) </details> <sub>Comment `@coderabbitai help` to get the list of available commands.</sub> <!-- tips_end -->
coderabbitai[bot] (Migrated from github.com) reviewed 2026-07-31 11:38:56 +05:30
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/main.go`:
- Around line 148-151: Update the Sentinel lockdown handling around lockdownTTL
so force=true consistently bypasses cached/stale responses and fetches from
Microsoft, subject to authentication or rate limiting; alternatively, explicitly
document in the /proxy contract that an active Sentinel lockdown overrides
force=true. Ensure the behavior and API contract no longer imply that
force=false is the only lockdown mode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 086d0222-523b-481d-b05d-c59a08f3f78e

📥 Commits

Reviewing files that changed from the base of the PR and between 5a806eea87 and 6ab849be5e.

📒 Files selected for processing (1)
  • backend/main.go
**Actionable comments posted: 1** <details> <summary>🤖 Prompt for all review comments with AI agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Inline comments: In `@backend/main.go`: - Around line 148-151: Update the Sentinel lockdown handling around lockdownTTL so force=true consistently bypasses cached/stale responses and fetches from Microsoft, subject to authentication or rate limiting; alternatively, explicitly document in the /proxy contract that an active Sentinel lockdown overrides force=true. Ensure the behavior and API contract no longer imply that force=false is the only lockdown mode. ``` </details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro Plus **Run ID**: `086d0222-523b-481d-b05d-c59a08f3f78e` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 5a806eea87ee76c8a903c912a1c70b4f2714f487 and 6ab849be5eff86eb2a474dd5f81a15cfb8c480d0. </details> <details> <summary>📒 Files selected for processing (1)</summary> * `backend/main.go` </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
coderabbitai[bot] (Migrated from github.com) commented 2026-07-31 11:38:55 +05:30

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,200p'

echo "== locate backend/main.go =="
fd -a 'main.go$' . || true
wc -l backend/main.go 2>/dev/null || true

echo "== outline backend/main.go =="
ast-grep outline backend/main.go --view expanded 2>/dev/null || true

echo "== relevant sections =="
sed -n '1,260p' backend/main.go

echo "== force references =="
rg -n "force|forceRefresh|force=true|LOCK|lockdown|Sentinel|429|stale|cache" backend/main.go

Repository: starkSV/windows-iso-downloader

Length of output: 26326


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== handleProxy full relevant section =="
sed -n '1173,1250p' backend/main.go

echo "== static verifier: forceRefresh before Sentinel lockdown path =="
python3 - <<'PY'
from pathlib import Path
import re
text = Path("backend/main.go").read_text()
m = re.search(r"func handleProxy\([^)]*\) \{", text)
start = m.start() if m else -1
# find brace block for handleProxy loosely
depth = 0
end = None
for i in range(start, len(text)):
    if text[i] == '{':
        if depth == 0:
            first = i
        depth += 1
    elif text[i] == '}':
        depth -= 1
        if depth == 0:
            end = i + 1
            break
print("handleProxy range:", start, end)
body = text[start:end]
checks = {
    "parse_force_refresh": "forceRefresh := r.URL.Query().Get(\"force\") == \"true\"" in body,
    "sentinel_lockdown_var": "lockdownEntry, hasLockdown := negCache[negKey]" in body,
    "sentinel_is_sentinel": "&& lockdownEntry.IsSentinel" in body,
}
for k,v in checks.items():
    print(k, v, "=>", checks[k] and "PASS" or "FAIL")
# locate nearby order by line indices.
force_line = next((line for line, txt in enumerate(body.splitlines(), 1) if "forceRefresh :=" in txt), None)
lockdown_key_line = next((line for line, txt in enumerate(body.splitlines(), 1) if "lockdownEntry, hasLockdown :=" in txt), None)
lockdown_check_line = next((line for line, txt in enumerate(body.splitlines(), 1) if "&& lockdownEntry.IsSentinel" in txt), None)
print("forceRefresh", force_line)
print("lockdownKey", lockdown_key_line)
print("isSentinelCheck", lockdown_check_line)
print("sentinel_before_force_refresh_gate", (not force_line) or (lockdown_key_line and lockdown_key_line < force_line))
PY

Repository: starkSV/windows-iso-downloader

Length of output: 3617


Remove force=false from the Sentinel lockdown contract.

The /proxy requirement says force=true must bypass cache and fetch from Microsoft, but the Sentinel lockdown gate serves cached/stale data or returns 429 even for force=true. Change the behavior so forced refresh bypasses Sentinel behind authentication or rate limiting, or document that active Sentinel lockdowns still override force=true.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/main.go` around lines 148 - 151, Update the Sentinel lockdown
handling around lockdownTTL so force=true consistently bypasses cached/stale
responses and fetches from Microsoft, subject to authentication or rate
limiting; alternatively, explicitly document in the /proxy contract that an
active Sentinel lockdown overrides force=true. Ensure the behavior and API
contract no longer imply that force=false is the only lockdown mode.

Source: Coding guidelines

_🎯 Functional Correctness_ | _🟠 Major_ | _⚡ Quick win_ <details> <summary>🧩 Analysis chain</summary> 🏁 Script executed: ```shell #!/bin/bash set -euo pipefail echo "== files ==" git ls-files | sed -n '1,200p' echo "== locate backend/main.go ==" fd -a 'main.go$' . || true wc -l backend/main.go 2>/dev/null || true echo "== outline backend/main.go ==" ast-grep outline backend/main.go --view expanded 2>/dev/null || true echo "== relevant sections ==" sed -n '1,260p' backend/main.go echo "== force references ==" rg -n "force|forceRefresh|force=true|LOCK|lockdown|Sentinel|429|stale|cache" backend/main.go ``` Repository: starkSV/windows-iso-downloader Length of output: 26326 --- 🏁 Script executed: ```shell #!/bin/bash set -euo pipefail echo "== handleProxy full relevant section ==" sed -n '1173,1250p' backend/main.go echo "== static verifier: forceRefresh before Sentinel lockdown path ==" python3 - <<'PY' from pathlib import Path import re text = Path("backend/main.go").read_text() m = re.search(r"func handleProxy\([^)]*\) \{", text) start = m.start() if m else -1 # find brace block for handleProxy loosely depth = 0 end = None for i in range(start, len(text)): if text[i] == '{': if depth == 0: first = i depth += 1 elif text[i] == '}': depth -= 1 if depth == 0: end = i + 1 break print("handleProxy range:", start, end) body = text[start:end] checks = { "parse_force_refresh": "forceRefresh := r.URL.Query().Get(\"force\") == \"true\"" in body, "sentinel_lockdown_var": "lockdownEntry, hasLockdown := negCache[negKey]" in body, "sentinel_is_sentinel": "&& lockdownEntry.IsSentinel" in body, } for k,v in checks.items(): print(k, v, "=>", checks[k] and "PASS" or "FAIL") # locate nearby order by line indices. force_line = next((line for line, txt in enumerate(body.splitlines(), 1) if "forceRefresh :=" in txt), None) lockdown_key_line = next((line for line, txt in enumerate(body.splitlines(), 1) if "lockdownEntry, hasLockdown :=" in txt), None) lockdown_check_line = next((line for line, txt in enumerate(body.splitlines(), 1) if "&& lockdownEntry.IsSentinel" in txt), None) print("forceRefresh", force_line) print("lockdownKey", lockdown_key_line) print("isSentinelCheck", lockdown_check_line) print("sentinel_before_force_refresh_gate", (not force_line) or (lockdown_key_line and lockdown_key_line < force_line)) PY ``` Repository: starkSV/windows-iso-downloader Length of output: 3617 --- </details> **Remove `force=false` from the Sentinel lockdown contract.** The `/proxy` requirement says `force=true` must bypass cache and fetch from Microsoft, but the Sentinel lockdown gate serves cached/stale data or returns `429` even for `force=true`. Change the behavior so forced refresh bypasses Sentinel behind authentication or rate limiting, or document that active Sentinel lockdowns still override `force=true`. <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/main.go` around lines 148 - 151, Update the Sentinel lockdown handling around lockdownTTL so force=true consistently bypasses cached/stale responses and fetches from Microsoft, subject to authentication or rate limiting; alternatively, explicitly document in the /proxy contract that an active Sentinel lockdown overrides force=true. Ensure the behavior and API contract no longer imply that force=false is the only lockdown mode. ``` </details> <!-- fingerprinting:phantom:triton:luna --> <!-- cr-indicator-types:potential_issue --> <!-- cr-comment:v1:d33717cda389f1bb5fb3bd8f --> _Source: Coding guidelines_ <!-- This is an auto-generated comment by CodeRabbit -->
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: shekhar/windows-iso-downloader#39
No description provided.