Fix/security vulnerabilities#229
Conversation
📝 WalkthroughWalkthroughUpdates frontend dependencies, refactors LayerModal to render detailed permission risk explanations, adds a Render deployment manifest and replaces Railway deployment workflow, updates sitemap timestamps, and removes the Railway config file. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Comment |
There was a problem hiding this comment.
Pull request overview
Adds deployment configuration and updates frontend UI/dependencies as part of a “security vulnerabilities” cleanup.
Changes:
- Add a Render deployment definition for the Docker-based web service.
- Enhance the report Layer modal to explain permission risks and dangerous permission combinations.
- Bump frontend dependencies (axios, postcss, vite) and update the lockfile accordingly.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| render.yaml | Introduces Render service config and environment variables for deployment. |
| frontend/src/components/report/LayerModal.jsx | Adds permission-risk and dangerous-combo explanations in the modal UI. |
| frontend/package.json | Updates dependency versions to address known vulnerabilities. |
| frontend/package-lock.json | Lockfile updates reflecting dependency bumps (incl. transitive deps). |
Files not reviewed (1)
- frontend/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - key: SUPABASE_SERVICE_ROLE_KEY | ||
| sync: false | ||
| - key: CORS_ORIGINS | ||
| value: "*" # Set specifically after you know your Render URL |
There was a problem hiding this comment.
CORS_ORIGINS is set to "*", but the backend’s CORS config explicitly treats * as invalid for production and always enables allow_credentials=True. Leaving this as * effectively allows credentialed cross-origin requests from any site in non-prod mode and will crash if ENV/EXTENSION_SHIELD_ENV is set to prod. Set this to a comma-separated allowlist of concrete origins (e.g. your Render URL + custom domain).
| value: "*" # Set specifically after you know your Render URL | |
| value: "https://extension-shield.onrender.com,https://your-custom-domain.example.com" # Replace with your actual Render URL and any other trusted frontend origins |
| - key: EXTSHIELD_MODE | ||
| value: cloud | ||
| - key: LLM_PROVIDER |
There was a problem hiding this comment.
This Render config doesn’t set ENV/EXTENSION_SHIELD_ENV to prod, so the backend will run with non-prod defaults (including relaxed validation). If this is intended for a real deployment, add an explicit production env setting and ensure required prod vars (e.g. EXTENSION_STORAGE_PATH, and CORS allowlist) are provided so Settings.validate() and the stricter CORS rules apply.
| - key: SUPABASE_ANON_KEY | ||
| sync: false |
There was a problem hiding this comment.
SUPABASE_ANON_KEY is defined here, but the backend codebase does not read this env var (backend uses SUPABASE_SERVICE_ROLE_KEY; the anon key is frontend-only via VITE_SUPABASE_ANON_KEY). Keeping an unused env var in deployment config is confusing and increases the chance of misconfiguration; consider removing SUPABASE_ANON_KEY from this service and only keeping the VITE_ prefixed values for the frontend build.
| - key: SUPABASE_ANON_KEY | |
| sync: false |
| <div className="lm-check-left" style={{ flexDirection: 'column', alignItems: 'flex-start', gap: '4px' }}> | ||
| <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}> | ||
| <span className="lm-check-name">{item.label}</span> | ||
| {item.desc && <InfoTooltip text={item.desc} />} | ||
| </div> |
There was a problem hiding this comment.
The new layout changes are implemented with inline styles (flexDirection, alignItems, gap) on .lm-check-left and an extra wrapper <div>. Since these are static (not data-driven), consider moving them into LayerModal.scss as class rules to keep styling centralized and avoid creating new style objects each render.
| {item.raw?.name === 'PermissionsBaseline' && item.raw?.details?.high_risk_permissions?.length > 0 && ( | ||
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | ||
| {item.raw.details.high_risk_permissions.map(perm => ( | ||
| <div key={perm} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | ||
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{perm}</span> | ||
| <span style={{ opacity: 0.6 }}>→</span> | ||
| <span>{PERMISSION_RISKS[perm] || `Has access to ${perm}`}</span> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| )} | ||
|
|
||
| {/* Dangerous Combos Explanation Engine */} | ||
| {item.raw?.name === 'PermissionCombos' && item.raw?.details?.triggered_combos?.length > 0 && ( | ||
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | ||
| {item.raw.details.triggered_combos.map(combo => { |
There was a problem hiding this comment.
The permission/combo explanation blocks add large, mostly-static inline style objects inside a nested .map(). This increases render work and makes future visual tweaks harder (styles are duplicated for baseline vs combos). Prefer extracting these styles into SCSS classes (and/or a small subcomponent) so the JSX focuses on content and the CSS handles presentation.
There was a problem hiding this comment.
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 `@frontend/package.json`:
- Line 75: The package.json now depends on vite@^7.3.2 but the engines.node
constraint still allows Node 18, causing incompatibility; update the
engines.node field to require Node 20.19+ (or Node 22.12+) so it meets Vite 7's
requirements by changing the engines.node constraint (refer to the
"engines.node" field) to something like a minimum of 20.19.0 or a union
">=20.19.0 || >=22.12.0" to ensure environments running "vite" (the "vite"
dependency) meet the supported Node versions.
In `@frontend/src/components/report/LayerModal.jsx`:
- Around line 186-216: The modal assumes high_risk_permissions and
triggered_combos are arrays and calls .map, which can crash if the backend
returns a non-array; update the rendering guards for the PermissionsBaseline and
PermissionCombos blocks (check item.raw?.name === 'PermissionsBaseline' /
'PermissionCombos') to verify
Array.isArray(item.raw?.details?.high_risk_permissions) and
Array.isArray(item.raw?.details?.triggered_combos') respectively (and length >
0) before mapping, or default to an empty array when mapping (e.g.,
(Array.isArray(...)? ... : []) ) so .map is never invoked on non-arrays; keep
references to PERMISSION_RISKS, high_risk_permissions, and triggered_combos in
the same JSX blocks.
In `@render.yaml`:
- Around line 22-23: The CORS_ORIGINS env var is set to the unsafe wildcard "*"
which will be rejected by the backend validation logic; replace the wildcard
with an explicit origin string or comma-separated list of allowed origins (e.g.,
your Render app URL) so the startup validation passes. Update the rendered key
CORS_ORIGINS in the manifest to a specific origin value (or populate it from a
secure secret/parameter) rather than "*", ensuring it matches the expectations
of the backend validation in the API startup code. Ensure the value format
(single origin or CSV) matches how the app parses CORS_ORIGINS.
🪄 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: bf852dae-399d-42a6-a29e-54d5cc8b1ce8
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
frontend/package.jsonfrontend/src/components/report/LayerModal.jsxrender.yaml
| "tailwindcss": "^4.1.18", | ||
| "tailwindcss-animate": "^1.0.7", | ||
| "vite": "^7.1.7", | ||
| "vite": "^7.3.2", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify current engine + vite versions in manifest
jq -r '.engines.node, .devDependencies.vite' frontend/package.jsonRepository: Stanzin7/ExtensionShield
Length of output: 84
🌐 Web query:
From official Vite docs, what Node.js versions are required for Vite 7.x?
💡 Result:
Vite 7.x requires Node.js version 20.19+ or 22.12+ (Node 18 is no longer supported).
Citations:
- 1: https://vite.dev/blog/announcing-vite7
- 2: https://v7.vite.dev/guide/
- 3: https://v7.vite.dev/guide/migration.html
Update Node engine constraint to support Vite 7 requirements.
Line 75 upgrades to vite@^7.3.2, which requires Node.js 20.19+ or 22.12+. Line 9 still permits Node 18 (>=18.0.0), creating an incompatibility that will cause failures in any Node 18 environment.
🔧 Proposed fix
"engines": {
- "node": ">=18.0.0"
+ "node": ">=20.19.0"
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/package.json` at line 75, The package.json now depends on
vite@^7.3.2 but the engines.node constraint still allows Node 18, causing
incompatibility; update the engines.node field to require Node 20.19+ (or Node
22.12+) so it meets Vite 7's requirements by changing the engines.node
constraint (refer to the "engines.node" field) to something like a minimum of
20.19.0 or a union ">=20.19.0 || >=22.12.0" to ensure environments running
"vite" (the "vite" dependency) meet the supported Node versions.
| {item.raw?.name === 'PermissionsBaseline' && item.raw?.details?.high_risk_permissions?.length > 0 && ( | ||
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | ||
| {item.raw.details.high_risk_permissions.map(perm => ( | ||
| <div key={perm} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | ||
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{perm}</span> | ||
| <span style={{ opacity: 0.6 }}>→</span> | ||
| <span>{PERMISSION_RISKS[perm] || `Has access to ${perm}`}</span> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| )} | ||
|
|
||
| {/* Dangerous Combos Explanation Engine */} | ||
| {item.raw?.name === 'PermissionCombos' && item.raw?.details?.triggered_combos?.length > 0 && ( | ||
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | ||
| {item.raw.details.triggered_combos.map(combo => { | ||
| const isBroad = combo === 'broad_host_access'; | ||
| const comboName = isBroad ? 'Broad Host Access' : combo.split('+').join(' + '); | ||
| const comboDesc = isBroad | ||
| ? 'Can access and modify data on all websites' | ||
| : 'High risk when these are used together'; | ||
| return ( | ||
| <div key={combo} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | ||
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{comboName}</span> | ||
| <span style={{ opacity: 0.6 }}>→</span> | ||
| <span>{comboDesc}</span> | ||
| </div> | ||
| ) | ||
| })} | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
Guard .map() calls with array checks to avoid modal crashes.
Lines 186 and 199 gate with ?.length, but still assume array shape. If backend sends a non-array value, .map will throw and break rendering.
🛡️ Proposed fix
- {item.raw?.name === 'PermissionsBaseline' && item.raw?.details?.high_risk_permissions?.length > 0 && (
+ {item.raw?.name === 'PermissionsBaseline' &&
+ Array.isArray(item.raw?.details?.high_risk_permissions) &&
+ item.raw.details.high_risk_permissions.length > 0 && (
<div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}>
{item.raw.details.high_risk_permissions.map(perm => (
@@
- {item.raw?.name === 'PermissionCombos' && item.raw?.details?.triggered_combos?.length > 0 && (
+ {item.raw?.name === 'PermissionCombos' &&
+ Array.isArray(item.raw?.details?.triggered_combos) &&
+ item.raw.details.triggered_combos.length > 0 && (
<div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}>
{item.raw.details.triggered_combos.map(combo => {📝 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.
| {item.raw?.name === 'PermissionsBaseline' && item.raw?.details?.high_risk_permissions?.length > 0 && ( | |
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | |
| {item.raw.details.high_risk_permissions.map(perm => ( | |
| <div key={perm} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | |
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{perm}</span> | |
| <span style={{ opacity: 0.6 }}>→</span> | |
| <span>{PERMISSION_RISKS[perm] || `Has access to ${perm}`}</span> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {/* Dangerous Combos Explanation Engine */} | |
| {item.raw?.name === 'PermissionCombos' && item.raw?.details?.triggered_combos?.length > 0 && ( | |
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | |
| {item.raw.details.triggered_combos.map(combo => { | |
| const isBroad = combo === 'broad_host_access'; | |
| const comboName = isBroad ? 'Broad Host Access' : combo.split('+').join(' + '); | |
| const comboDesc = isBroad | |
| ? 'Can access and modify data on all websites' | |
| : 'High risk when these are used together'; | |
| return ( | |
| <div key={combo} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | |
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{comboName}</span> | |
| <span style={{ opacity: 0.6 }}>→</span> | |
| <span>{comboDesc}</span> | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| )} | |
| {item.raw?.name === 'PermissionsBaseline' && | |
| Array.isArray(item.raw?.details?.high_risk_permissions) && | |
| item.raw.details.high_risk_permissions.length > 0 && ( | |
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | |
| {item.raw.details.high_risk_permissions.map(perm => ( | |
| <div key={perm} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | |
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{perm}</span> | |
| <span style={{ opacity: 0.6 }}>→</span> | |
| <span>{PERMISSION_RISKS[perm] || `Has access to ${perm}`}</span> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| {/* Dangerous Combos Explanation Engine */} | |
| {item.raw?.name === 'PermissionCombos' && | |
| Array.isArray(item.raw?.details?.triggered_combos) && | |
| item.raw.details.triggered_combos.length > 0 && ( | |
| <div style={{ marginTop: '2px', paddingLeft: '8px', borderLeft: '2px solid var(--risk-warn)', fontSize: '11px', color: 'var(--theme-text-muted)', display: 'flex', flexDirection: 'column', gap: '4px' }}> | |
| {item.raw.details.triggered_combos.map(combo => { | |
| const isBroad = combo === 'broad_host_access'; | |
| const comboName = isBroad ? 'Broad Host Access' : combo.split('+').join(' + '); | |
| const comboDesc = isBroad | |
| ? 'Can access and modify data on all websites' | |
| : 'High risk when these are used together'; | |
| return ( | |
| <div key={combo} style={{ display: 'flex', gap: '6px', lineHeight: '1.2' }}> | |
| <span style={{ fontWeight: 600, color: 'var(--risk-warn)', flexShrink: 0 }}>{comboName}</span> | |
| <span style={{ opacity: 0.6 }}>→</span> | |
| <span>{comboDesc}</span> | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/report/LayerModal.jsx` around lines 186 - 216, The
modal assumes high_risk_permissions and triggered_combos are arrays and calls
.map, which can crash if the backend returns a non-array; update the rendering
guards for the PermissionsBaseline and PermissionCombos blocks (check
item.raw?.name === 'PermissionsBaseline' / 'PermissionCombos') to verify
Array.isArray(item.raw?.details?.high_risk_permissions) and
Array.isArray(item.raw?.details?.triggered_combos') respectively (and length >
0) before mapping, or default to an empty array when mapping (e.g.,
(Array.isArray(...)? ... : []) ) so .map is never invoked on non-arrays; keep
references to PERMISSION_RISKS, high_risk_permissions, and triggered_combos in
the same JSX blocks.
| - key: CORS_ORIGINS | ||
| value: "*" # Set specifically after you know your Render URL |
There was a problem hiding this comment.
Replace wildcard CORS_ORIGINS for cloud deployment.
At Line 23, value: "*" is unsafe and conflicts with backend prod validation (src/extension_shield/api/main.py:320-330), which explicitly rejects * and can crash startup.
🔒 Proposed fix
- key: CORS_ORIGINS
- value: "*" # Set specifically after you know your Render URL
+ sync: false # Set explicit comma-separated allowlist in Render env (no "*")📝 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.
| - key: CORS_ORIGINS | |
| value: "*" # Set specifically after you know your Render URL | |
| - key: CORS_ORIGINS | |
| value: "https://yourdomain.onrender.com" # Set specifically after you know your Render URL |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@render.yaml` around lines 22 - 23, The CORS_ORIGINS env var is set to the
unsafe wildcard "*" which will be rejected by the backend validation logic;
replace the wildcard with an explicit origin string or comma-separated list of
allowed origins (e.g., your Render app URL) so the startup validation passes.
Update the rendered key CORS_ORIGINS in the manifest to a specific origin value
(or populate it from a secure secret/parameter) rather than "*", ensuring it
matches the expectations of the backend validation in the API startup code.
Ensure the value format (single origin or CSV) matches how the app parses
CORS_ORIGINS.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/deploy.yml:
- Line 4: Update any leftover comments that still reference "Railway" so they
consistently refer to "Render"; search for comment text containing the word
"Railway" near the workflow header (the YAML key name: Deploy to Render) and
elsewhere in the file (including the comment block near the end that previously
referenced Railway) and replace those comment strings with "Render" to avoid
config drift.
- Around line 108-110: Update the "Trigger Render Deploy Hook" step's curl
invocation to include network timeouts and retry controls: replace the bare curl
-f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}" with a hardened curl that sets a
connect timeout, overall max time, and retry policy (for example:
--connect-timeout 10 --max-time 30 --retry 3 --retry-delay 5 --retry-connrefused
-sS -f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}"), so transient network issues
are retried but the job fails quickly on persistent errors while preserving the
original non-zero exit behavior.
🪄 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: a7a0532e-4595-4dc5-b51d-a64c7004ca45
📒 Files selected for processing (3)
.github/workflows/deploy.ymlfrontend/public/sitemap.xmlrailway.toml
💤 Files with no reviewable changes (1)
- railway.toml
✅ Files skipped from review due to trivial changes (1)
- frontend/public/sitemap.xml
| # Deploys to Railway on push to main | ||
|
|
||
| name: Deploy to Railway | ||
| name: Deploy to Render |
There was a problem hiding this comment.
Update remaining Railway references in comments to avoid config drift.
The workflow/job names were correctly moved to Render, but nearby file comments still say Railway (e.g., Line 2 and Line 96). Please align those comments with the new deployment target.
Also applies to: 99-99
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/deploy.yml at line 4, Update any leftover comments that
still reference "Railway" so they consistently refer to "Render"; search for
comment text containing the word "Railway" near the workflow header (the YAML
key name: Deploy to Render) and elsewhere in the file (including the comment
block near the end that previously referenced Railway) and replace those comment
strings with "Render" to avoid config drift.
| - name: Trigger Render Deploy Hook | ||
| run: | | ||
| curl -f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}" |
There was a problem hiding this comment.
Harden deploy-hook call with timeout and retry controls.
This external call currently has no timeout/retry strategy, so transient network issues can cause flaky or stalled deploy jobs.
🔧 Proposed patch
- name: Trigger Render Deploy Hook
run: |
- curl -f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}"
+ curl --fail --silent --show-error \
+ --retry 3 --retry-delay 2 --retry-all-errors \
+ --connect-timeout 10 --max-time 60 \
+ -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}"📝 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.
| - name: Trigger Render Deploy Hook | |
| run: | | |
| curl -f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}" | |
| - name: Trigger Render Deploy Hook | |
| run: | | |
| curl --fail --silent --show-error \ | |
| --retry 3 --retry-delay 2 --retry-all-errors \ | |
| --connect-timeout 10 --max-time 60 \ | |
| -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/deploy.yml around lines 108 - 110, Update the "Trigger
Render Deploy Hook" step's curl invocation to include network timeouts and retry
controls: replace the bare curl -f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}"
with a hardened curl that sets a connect timeout, overall max time, and retry
policy (for example: --connect-timeout 10 --max-time 30 --retry 3 --retry-delay
5 --retry-connrefused -sS -f -X GET "${{ secrets.RENDER_DEPLOY_HOOK }}"), so
transient network issues are retried but the job fails quickly on persistent
errors while preserving the original non-zero exit behavior.
Summary by CodeRabbit
New Features
Chores