Added Social Media Manager Dashboard with Lilac Theme & Wizard Wand Cursor#2
Added Social Media Manager Dashboard with Lilac Theme & Wizard Wand Cursor#2mitanuriel merged 3 commits intomainfrom
Conversation
- Created responsive single-page dashboard for social media post generation - Integrated with existing FastAPI backend - Features: API key storage, multiple model selection, copy to clipboard - Modern dark theme with animations and smooth UX - Added run_server.py for easy startup - Includes comprehensive dashboard documentation
- Transformed UI with soft lilac/purple color palette and gold accents - Implemented magical wizard wand cursor with instant response (no delay) - Added Playfair Display font for elegant, high-contrast typography - Enhanced custom cursor with sparkle trail effects - Improved API error handling with detailed user-friendly messages - Added debug logging for API key validation - Created test_api_key.py utility for troubleshooting Mistral API - Optimized cursor follower speed (45% interpolation for smooth tracking) - Added glowing effects and animations throughout the dashboard
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (7)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
|
@coderabbitai help |
ChatThere are 3 ways to chat with CodeRabbit:
CodeRabbit commands
Other keywords and placeholders
CodeRabbit configuration file (
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/social_media_backend.py (1)
1-110:⚠️ Potential issue | 🟡 MinorRun Black on this module to unblock CI.
The pipeline reports a Black formatting failure, and this file appears to be the one requiring reformatting.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/social_media_backend.py` around lines 1 - 110, This module (contains MISTRAL_CHAT_COMPLETIONS_URL, class GeneratePostRequest, and function generate_post) fails Black formatting; run Black on the file containing those symbols (or run black . at repo root), reformat the file, stage the changes, and commit so CI passes; ensure no other logic is changed—only apply Black's automatic formatting.
🧹 Nitpick comments (1)
src/static/styles.css (1)
71-74: Add reduced-motion handling for continuous animations.Please honor user
prefers-reduced-motionsettings for pulse/twinkle/sparkle/shake/spin effects.Suggested fix
+@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation: none !important; + transition: none !important; + } +}Also applies to: 89-92, 114-117, 230-265, 301-301
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/static/styles.css` around lines 71 - 74, The wand-glow keyframes (and other continuous animations like pulse, twinkle, sparkle, shake, spin) must respect the user's prefers-reduced-motion setting; add a CSS media query `@media` (prefers-reduced-motion: reduce) that zeroes or removes animation by overriding animation/animation-duration/animation-iteration-count for elements using wand-glow (and the pulse, twinkle, sparkle, shake, spin classes or animation names) and/or replaces them with a non-animated static state, and ensure keyframe definitions remain but are not applied under the media query so the final visual state is preserved (i.e., set transform/opacity to the desired resting values and animation: none or animation-duration: 0s for selectors that reference these animations).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@run_server.py`:
- Around line 8-10: Change the default server bind and autoreload behavior so
startup is safer: stop hardcoding host="0.0.0.0" and reload=True; instead
default host to "127.0.0.1" (or "localhost") and set reload to False, and make
both overrideable via environment variables (e.g., BIND_HOST / BIND_RELOAD or
similar) that are read before calling uvicorn.run so the uvicorn.run(host=...,
port=8000, reload=...) call uses the env-controlled values.
In `@src/social_media_backend.py`:
- Around line 48-50: Remove the debugging print that exposes API key fragments
and length: delete or replace the lines constructing key_preview and the print
statement that references clean_api_key, key_preview, or len(clean_api_key) so
no secret-derived value is logged; if a debug marker is needed use a constant
placeholder (e.g., "<REDACTED_API_KEY>") or log only non-secret request metadata
instead; update any references to key_preview in the surrounding function (e.g.,
where clean_api_key is processed) to avoid using the removed variable.
- Line 68: Create a small helper function (e.g., safe_parse_json(response)) that
attempts response.json() and catches json.JSONDecodeError (and ValueError for
some clients), returning the parsed dict on success or None on failure; then
replace direct response.json() calls used to build error_msg (the assignment
currently using response.json().get("message", "") if response.text else "") and
the later unprotected response.json() usage with the helper, falling back to
response.text or an empty string when safe_parse_json returns None, so
HTTPException construction always uses a string and never raises on non-JSON
bodies.
In `@src/static/app.js`:
- Line 18: Current code persists API keys in localStorage by default using
STORAGE_KEY; change this to use sessionStorage by default and make localStorage
persistence explicit and opt-in: update any get/load and save logic that
currently calls localStorage.getItem(STORAGE_KEY) or
localStorage.setItem(STORAGE_KEY, ...) to instead use sessionStorage for normal
load/save, add a user-controlled "Remember API key" checkbox (or flag) that when
checked will also write the key to localStorage and read from it on startup, and
ensure that on startup you only restore from localStorage if that opt-in flag is
present; keep the STORAGE_KEY identifier but gate localStorage access behind the
new opt-in flag and leave sessionStorage as the default storage backend.
In `@src/static/index.html`:
- Line 110: The external anchor tag that opens a new tab (the <a> element
linking to "https://github.com/mitanuriel/agentSecOps_Guardian" using
target="_blank") is missing a rel attribute and is vulnerable to
reverse-tabnabbing; update that anchor to include rel="noopener noreferrer" so
external pages cannot access window.opener and to improve security.
In `@src/static/styles.css`:
- Around line 29-37: The global CSS currently hides the native cursor via the
body selector (cursor: none) which creates an accessibility blocker if the
custom JS cursor fails; remove or guard that rule and switch to applying cursor:
none only when a class (e.g., custom-cursor-enabled) is present on body, update
any button selectors that set cursor: none (the same rule affecting buttons at
the places noted) to fall back to the native cursor unless the class is present,
and in your JS (e.g., app.js) add logic to add
document.body.classList.add('custom-cursor-enabled') only after the custom
cursor DOM and event handlers are fully initialized so the native cursor remains
as a fallback until the custom cursor is ready.
In `@test_api_key.py`:
- Around line 12-15: Remove any printing of API key metadata/preview and stop
recommending passing secrets via command-line arguments: eliminate the prints
that reference api_key (the f-strings that output length and preview) and any
CLI usage guidance around supplying the key; instead read the key from an
environment variable (os.environ.get("API_KEY")) or prompt securely
(getpass.getpass) when api_key is missing, and log only non-secret status
messages (e.g., "API key provided" or "API key missing") without revealing
length or characters; update any code paths or docs around the CLI to prefer env
var or interactive prompt rather than showing the secret in shell history or
process lists.
---
Outside diff comments:
In `@src/social_media_backend.py`:
- Around line 1-110: This module (contains MISTRAL_CHAT_COMPLETIONS_URL, class
GeneratePostRequest, and function generate_post) fails Black formatting; run
Black on the file containing those symbols (or run black . at repo root),
reformat the file, stage the changes, and commit so CI passes; ensure no other
logic is changed—only apply Black's automatic formatting.
---
Nitpick comments:
In `@src/static/styles.css`:
- Around line 71-74: The wand-glow keyframes (and other continuous animations
like pulse, twinkle, sparkle, shake, spin) must respect the user's
prefers-reduced-motion setting; add a CSS media query `@media`
(prefers-reduced-motion: reduce) that zeroes or removes animation by overriding
animation/animation-duration/animation-iteration-count for elements using
wand-glow (and the pulse, twinkle, sparkle, shake, spin classes or animation
names) and/or replaces them with a non-animated static state, and ensure
keyframe definitions remain but are not applied under the media query so the
final visual state is preserved (i.e., set transform/opacity to the desired
resting values and animation: none or animation-duration: 0s for selectors that
reference these animations).
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
DASHBOARD.mdrun_server.pysrc/social_media_backend.pysrc/static/app.jssrc/static/index.htmlsrc/static/styles.csstest_api_key.py
| host="0.0.0.0", | ||
| port=8000, | ||
| reload=True, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find run_server.py and check its existence
find . -name "run_server.py" -type fRepository: mitanuriel/agentSecOps_Guardian
Length of output: 90
🏁 Script executed:
# List files in the repo to understand structure
git ls-files | head -20Repository: mitanuriel/agentSecOps_Guardian
Length of output: 385
🏁 Script executed:
# Check for Docker, deployment, or configuration files
git ls-files | grep -E "(Dockerfile|docker-compose|\.env|\.yaml|\.yml|requirements|setup\.py|pyproject\.toml)"Repository: mitanuriel/agentSecOps_Guardian
Length of output: 174
🏁 Script executed:
# Read run_server.py to verify the exact code
cat -n run_server.pyRepository: mitanuriel/agentSecOps_Guardian
Length of output: 417
🏁 Script executed:
# Check .env.example to see what env vars are already documented
cat -n .env.exampleRepository: mitanuriel/agentSecOps_Guardian
Length of output: 474
🏁 Script executed:
# Check the Dockerfile if it exists
find . -name "Dockerfile*" -type f -exec cat -n {} \;Repository: mitanuriel/agentSecOps_Guardian
Length of output: 57
🏁 Script executed:
# Check CI workflow to see how the app is intended to be started
cat -n .github/workflows/ci.ymlRepository: mitanuriel/agentSecOps_Guardian
Length of output: 1915
🏁 Script executed:
# Check README for deployment/startup instructions
cat README.mdRepository: mitanuriel/agentSecOps_Guardian
Length of output: 3889
Restrict default bind/reload settings for safer startup.
Line 8 binds to all interfaces and Line 10 enables reload by default. Even for development, binding to 0.0.0.0 exposes the server to all network interfaces, which is risky on shared networks. Default to localhost and make external bind and reload opt-in via environment variables.
Suggested fix
+import os
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"src.social_media_backend:app",
- host="0.0.0.0",
+ host=os.getenv("HOST", "127.0.0.1"),
port=8000,
- reload=True,
+ reload=os.getenv("UVICORN_RELOAD", "false").lower() == "true",
log_level="info"
)📝 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.
| host="0.0.0.0", | |
| port=8000, | |
| reload=True, | |
| import os | |
| import uvicorn | |
| if __name__ == "__main__": | |
| uvicorn.run( | |
| "src.social_media_backend:app", | |
| host=os.getenv("HOST", "127.0.0.1"), | |
| port=8000, | |
| reload=os.getenv("UVICORN_RELOAD", "false").lower() == "true", | |
| log_level="info" | |
| ) |
🧰 Tools
🪛 Ruff (0.15.2)
[error] 8-8: Possible binding to all interfaces
(S104)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@run_server.py` around lines 8 - 10, Change the default server bind and
autoreload behavior so startup is safer: stop hardcoding host="0.0.0.0" and
reload=True; instead default host to "127.0.0.1" (or "localhost") and set reload
to False, and make both overrideable via environment variables (e.g., BIND_HOST
/ BIND_RELOAD or similar) that are read before calling uvicorn.run so the
uvicorn.run(host=..., port=8000, reload=...) call uses the env-controlled
values.
| # Log key format for debugging (first/last 4 chars only) | ||
| key_preview = f"{clean_api_key[:4]}...{clean_api_key[-4:]}" if len(clean_api_key) > 8 else "***" | ||
| print(f"DEBUG: Using API key: {key_preview}, length: {len(clean_api_key)}") |
There was a problem hiding this comment.
Remove API-key fingerprint logging from request path.
Lines 48-50 log key preview and length. Secret-derived values should not be emitted to logs.
Suggested fix
- # Log key format for debugging (first/last 4 chars only)
- key_preview = f"{clean_api_key[:4]}...{clean_api_key[-4:]}" if len(clean_api_key) > 8 else "***"
- print(f"DEBUG: Using API key: {key_preview}, length: {len(clean_api_key)}")📝 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.
| # Log key format for debugging (first/last 4 chars only) | |
| key_preview = f"{clean_api_key[:4]}...{clean_api_key[-4:]}" if len(clean_api_key) > 8 else "***" | |
| print(f"DEBUG: Using API key: {key_preview}, length: {len(clean_api_key)}") |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/social_media_backend.py` around lines 48 - 50, Remove the debugging print
that exposes API key fragments and length: delete or replace the lines
constructing key_preview and the print statement that references clean_api_key,
key_preview, or len(clean_api_key) so no secret-derived value is logged; if a
debug marker is needed use a constant placeholder (e.g., "<REDACTED_API_KEY>")
or log only non-secret request metadata instead; update any references to
key_preview in the surrounding function (e.g., where clean_api_key is processed)
to avoid using the removed variable.
| # Handle API errors with helpful messages | ||
| if response.status_code == 401: | ||
| from fastapi import HTTPException | ||
| error_msg = response.json().get("message", "") if response.text else "" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/social_media_backend.py | sed -n '50,100p'Repository: mitanuriel/agentSecOps_Guardian
Length of output: 2420
Safely parse non-JSON error responses before building HTTP errors.
Lines 68 and 82 call response.json() directly without handling potential json.JSONDecodeError. If the upstream API returns an error response with a non-JSON body, these calls will raise an unhandled exception, bypassing your intended HTTPException handling. Line 68's check of response.text only verifies content exists, not that it's valid JSON. Line 82 has no protection whatsoever.
Create a helper function to safely parse responses:
Suggested fix
+def _safe_json(response: requests.Response) -> dict:
+ try:
+ return response.json()
+ except ValueError:
+ return {}
+
if response.status_code == 401:
from fastapi import HTTPException
- error_msg = response.json().get("message", "") if response.text else ""
+ body = _safe_json(response)
+ error_msg = body.get("message", "") if response.text else ""
print(f"DEBUG: 401 error details - {error_msg}")
elif response.status_code >= 400:
from fastapi import HTTPException
- error_detail = response.json().get("error", {}).get("message", "Unknown error")
+ body = _safe_json(response)
+ error_detail = body.get("error", {}).get("message", "Unknown error")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/social_media_backend.py` at line 68, Create a small helper function
(e.g., safe_parse_json(response)) that attempts response.json() and catches
json.JSONDecodeError (and ValueError for some clients), returning the parsed
dict on success or None on failure; then replace direct response.json() calls
used to build error_msg (the assignment currently using
response.json().get("message", "") if response.text else "") and the later
unprotected response.json() usage with the helper, falling back to response.text
or an empty string when safe_parse_json returns None, so HTTPException
construction always uses a string and never raises on non-JSON bodies.
| const API_BASE_URL = window.location.origin; | ||
|
|
||
| // Local Storage for API Key | ||
| const STORAGE_KEY = 'mistral_api_key'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/static/app.jsRepository: mitanuriel/agentSecOps_Guardian
Length of output: 7903
Remove default localStorage persistence for API keys; use sessionStorage by default.
The code stores API keys to localStorage (lines 95-97) and reloads them on startup (lines 74-79) without any user opt-in. This exposes long-lived secrets to XSS attacks on the same origin. Use sessionStorage by default and make localStorage persistence an explicit, user-controlled opt-in via a checkbox.
Suggested fix
const STORAGE_KEY = 'mistral_api_key';
+const REMEMBER_KEY = false; // wire to an explicit "Remember key" checkbox
// Load saved API key on page load
window.addEventListener('DOMContentLoaded', () => {
- const savedKey = localStorage.getItem(STORAGE_KEY);
+ const savedKey = sessionStorage.getItem(STORAGE_KEY) || localStorage.getItem(STORAGE_KEY);
if (savedKey) {
document.getElementById('apiKey').value = savedKey;
}
});
@@
- // Save API key to local storage
- localStorage.setItem(STORAGE_KEY, apiKey);
+ if (REMEMBER_KEY) {
+ localStorage.setItem(STORAGE_KEY, apiKey);
+ } else {
+ sessionStorage.setItem(STORAGE_KEY, apiKey);
+ localStorage.removeItem(STORAGE_KEY);
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/static/app.js` at line 18, Current code persists API keys in localStorage
by default using STORAGE_KEY; change this to use sessionStorage by default and
make localStorage persistence explicit and opt-in: update any get/load and save
logic that currently calls localStorage.getItem(STORAGE_KEY) or
localStorage.setItem(STORAGE_KEY, ...) to instead use sessionStorage for normal
load/save, add a user-controlled "Remember API key" checkbox (or flag) that when
checked will also write the key to localStorage and read from it on startup, and
ensure that on startup you only restore from localStorage if that opt-in flag is
present; keep the STORAGE_KEY identifier but gate localStorage access behind the
new opt-in flag and leave sessionStorage as the default storage backend.
| </main> | ||
|
|
||
| <footer> | ||
| <p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank">GitHub</a></p> |
There was a problem hiding this comment.
Add rel="noopener noreferrer" to external links opened with _blank.
Line 110 uses target="_blank" without rel, which allows reverse-tabnabbing.
Suggested fix
-<p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank">GitHub</a></p>
+<p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank" rel="noopener noreferrer">GitHub</a></p>📝 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.
| <p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank">GitHub</a></p> | |
| <p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank" rel="noopener noreferrer">GitHub</a></p> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/static/index.html` at line 110, The external anchor tag that opens a new
tab (the <a> element linking to
"https://github.com/mitanuriel/agentSecOps_Guardian" using target="_blank") is
missing a rel attribute and is vulnerable to reverse-tabnabbing; update that
anchor to include rel="noopener noreferrer" so external pages cannot access
window.opener and to improve security.
| body { | ||
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | ||
| background: linear-gradient(135deg, #1e1b2e 0%, #2d1b3d 50%, #1e1b2e 100%); | ||
| color: var(--text-primary); | ||
| min-height: 100vh; | ||
| padding: 20px; | ||
| line-height: 1.6; | ||
| cursor: none; | ||
| } |
There was a problem hiding this comment.
Keep native cursor as fallback to avoid an accessibility blocker.
Global cursor: none (Line 36) plus button-level cursor: none (Lines 187 and 222) hides pointer affordance if custom JS cursor fails/loads late. Hide cursor only after JS explicitly enables custom-cursor mode.
Suggested fix
body {
@@
- cursor: none;
+ cursor: auto;
}
+
+body.custom-cursor-enabled {
+ cursor: none;
+}
@@
.btn-primary, .btn-secondary {
@@
- cursor: none;
+ cursor: pointer;
@@
}
@@
-.btn-primary:disabled { opacity: 0.6; cursor: none; transform: none; }
+.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }// additionally in app.js (after custom cursor elements are added)
document.body.classList.add('custom-cursor-enabled');Also applies to: 180-188, 222-222
🧰 Tools
🪛 Stylelint (17.3.0)
[error] 30-30: Unexpected quotes around "Inter" (font-family-name-quotes)
(font-family-name-quotes)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/static/styles.css` around lines 29 - 37, The global CSS currently hides
the native cursor via the body selector (cursor: none) which creates an
accessibility blocker if the custom JS cursor fails; remove or guard that rule
and switch to applying cursor: none only when a class (e.g.,
custom-cursor-enabled) is present on body, update any button selectors that set
cursor: none (the same rule affecting buttons at the places noted) to fall back
to the native cursor unless the class is present, and in your JS (e.g., app.js)
add logic to add document.body.classList.add('custom-cursor-enabled') only after
the custom cursor DOM and event handlers are fully initialized so the native
cursor remains as a fallback until the custom cursor is ready.
| print(f"Testing API key...") | ||
| print(f" Length: {len(api_key)} characters") | ||
| print(f" Preview: {api_key[:4]}...{api_key[-4:]}" if len(api_key) > 8 else " Key too short!") | ||
| print() |
There was a problem hiding this comment.
Avoid leaking key material via logs and CLI arguments.
Lines 12-15 print key metadata/preview, and Lines 56-60 encourage passing secrets via CLI (visible in shell history/process list). Prefer env var or interactive prompt and remove preview logging.
Suggested fix
- print(f"Testing API key...")
- print(f" Length: {len(api_key)} characters")
- print(f" Preview: {api_key[:4]}...{api_key[-4:]}" if len(api_key) > 8 else " Key too short!")
+ print("Testing API key...")
print()
@@
- print("Usage: python test_api_key.py YOUR_API_KEY")
+ print("Usage: MISTRAL_API_KEY='your-key-here' python test_api_key.py")Also applies to: 56-60
🧰 Tools
🪛 Ruff (0.15.2)
[error] 12-12: f-string without any placeholders
Remove extraneous f prefix
(F541)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test_api_key.py` around lines 12 - 15, Remove any printing of API key
metadata/preview and stop recommending passing secrets via command-line
arguments: eliminate the prints that reference api_key (the f-strings that
output length and preview) and any CLI usage guidance around supplying the key;
instead read the key from an environment variable (os.environ.get("API_KEY")) or
prompt securely (getpass.getpass) when api_key is missing, and log only
non-secret status messages (e.g., "API key provided" or "API key missing")
without revealing length or characters; update any code paths or docs around the
CLI to prefer env var or interactive prompt rather than showing the secret in
shell history or process lists.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (7)
run_server.py (1)
8-10:⚠️ Potential issue | 🟠 MajorHarden default bind/reload behavior for safer local startup.
Line 8 binds to all interfaces and Line 10 enables reload unconditionally. Keep localhost and reload-off as defaults, and make both opt-in via env vars.
Suggested fix
+import os import uvicorn if __name__ == "__main__": + host = os.getenv("BIND_HOST", "127.0.0.1") + reload_enabled = os.getenv("BIND_RELOAD", "false").lower() == "true" uvicorn.run( "src.social_media_backend:app", - host="0.0.0.0", + host=host, port=8000, - reload=True, + reload=reload_enabled, log_level="info" )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@run_server.py` around lines 8 - 10, Change the hardcoded uvicorn startup args to safer defaults and make binding/reload opt-in: set host default to "127.0.0.1" instead of "0.0.0.0" and keep reload disabled by default; read an environment variable (e.g., START_PUBLIC or BIND_ALL) to allow binding to 0.0.0.0 and another env var (e.g., ENABLE_RELOAD or DEV_RELOAD) to enable reload. Update the uvicorn.run(...) call (the block that currently passes host="0.0.0.0", port=8000, reload=True) to use os.getenv checks to choose host and reload values, and document or name the env vars clearly so startup remains localhost/no-reload unless explicitly opted-in.src/static/index.html (1)
110-110:⚠️ Potential issue | 🟠 MajorAdd
rel="noopener noreferrer"on external_blanklink.Line 110 opens a new tab without
rel, which leaves reverse-tabnabbing exposure.Suggested fix
-<p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank">GitHub</a></p> +<p>Built for Mistral AI Hackathon 2026 | <a href="https://github.com/mitanuriel/agentSecOps_Guardian" target="_blank" rel="noopener noreferrer">GitHub</a></p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/static/index.html` at line 110, The external anchor link with href "https://github.com/mitanuriel/agentSecOps_Guardian" that opens in a new tab (target="_blank") should include rel="noopener noreferrer" to prevent reverse-tabnabbing; update the <a> element for that GitHub link in src/static/index.html to add rel="noopener noreferrer" while preserving the existing target and link text.src/social_media_backend.py (2)
68-68:⚠️ Potential issue | 🟠 MajorSafely parse upstream error bodies before reading JSON fields.
Line 68 and Line 82 call
response.json()directly. Non-JSON error responses will raise and bypass intendedHTTPExceptionhandling.Suggested fix
+def _safe_json(response: requests.Response) -> dict: + try: + return response.json() + except ValueError: + return {} + @@ if response.status_code == 401: from fastapi import HTTPException - error_msg = response.json().get("message", "") if response.text else "" + error_msg = _safe_json(response).get("message", "") if response.text else "" print(f"DEBUG: 401 error details - {error_msg}") @@ elif response.status_code >= 400: from fastapi import HTTPException - error_detail = response.json().get("error", {}).get("message", "Unknown error") + error_detail = _safe_json(response).get("error", {}).get("message", "Unknown error")Also applies to: 82-82
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/social_media_backend.py` at line 68, The code calls response.json() directly when building error_msg (e.g., the variable error_msg and the error-handling blocks around those response usages), which will raise on non-JSON error bodies; instead, guard JSON parsing by checking Content-Type for application/json or wrapping response.json() in a try/except (catch ValueError/JSONDecodeError) and fall back to response.text (or an empty string) before raising the HTTPException — update both places that call response.json() so they safely parse or fallback and include the safe message in the HTTPException.
48-50:⚠️ Potential issue | 🟠 MajorRemove API-key fingerprint logging from request flow.
Line 50 logs secret-derived key preview/length. This should not be emitted in server logs.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/social_media_backend.py` around lines 48 - 50, Remove the secret-derived API key preview and length logging: delete the key_preview computation and the print statement that references clean_api_key (the lines computing key_preview and the print(f"DEBUG: Using API key...")). If you still need a trace, replace with a non-sensitive log such as a single message like "DEBUG: API key provided" or no-op, but do not include clean_api_key, key_preview, or its length anywhere in logs; update any callers in this module that rely on key_preview accordingly.src/static/styles.css (1)
29-37:⚠️ Potential issue | 🟠 MajorKeep native cursor fallback unless custom cursor is explicitly enabled.
Global and button-level
cursor: nonecreates an accessibility blocker when custom cursor init fails/loads late. Gate cursor-hiding behind a body class enabled by JS after cursor setup.Suggested fix
body { @@ - cursor: none; + cursor: auto; } + +body.custom-cursor-enabled { + cursor: none; +} @@ .btn-primary, .btn-secondary { @@ - cursor: none; + cursor: pointer; @@ } @@ -.btn-primary:disabled { opacity: 0.6; cursor: none; transform: none; } +.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }Also add in
src/static/app.jsright after cursor/follower setup:document.body.classList.add('custom-cursor-enabled');Also applies to: 180-188, 222-222
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/static/styles.css` around lines 29 - 37, The global CSS rule hiding the cursor on the body (`body { cursor: none; }`) is unsafe; remove that global `cursor: none` and instead add a scoped rule that only hides the cursor when a JS-enabled flag class is present (e.g. create a `.custom-cursor-enabled` selector that applies `cursor: none` to `body` and any button selectors that previously relied on the global rule). Then, in your cursor initialization code in app.js, after the cursor/follower setup completes, add a call to add the flag class to the document body (use `document.body.classList.add('custom-cursor-enabled')`) so the custom cursor is only hidden if JS successfully initialized it.test_api_key.py (1)
12-15:⚠️ Potential issue | 🟠 MajorStop leaking API-key metadata and avoid CLI-secret input.
Line 13–14 exposes key-derived metadata, and Line 70 reads secrets from argv (visible in shell history/process listings). Prefer env var or secure prompt only.
Suggested fix
+import os +import getpass import sys import requests @@ def test_mistral_key(api_key: str) -> None: @@ - print(f"Testing API key...") - print(f" Length: {len(api_key)} characters") - print(f" Preview: {api_key[:4]}...{api_key[-4:]}" if len(api_key) > 8 else " Key too short!") + print("Testing API key...") print() @@ if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: python test_api_key.py YOUR_API_KEY") - print("\nOr set MISTRAL_API_KEY environment variable:") - print(" export MISTRAL_API_KEY='your-key-here'") - print(" python test_api_key.py") - - import os - api_key = os.getenv("MISTRAL_API_KEY") - if api_key: - print("\nFound MISTRAL_API_KEY in environment, testing...") - test_mistral_key(api_key) - else: - sys.exit(1) - else: - test_mistral_key(sys.argv[1]) + api_key = os.getenv("MISTRAL_API_KEY") or getpass.getpass("Enter Mistral API key: ") + if not api_key: + sys.exit(1) + test_mistral_key(api_key)Also applies to: 56-60, 70-70
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test_api_key.py` around lines 12 - 15, Remove any CLI printing of API key metadata (the prints that reference api_key, its length, and preview) so the test does not leak secrets; instead validate without logging key content. Replace reading secrets from argv (where sys.argv or argparse parses the secret at the noted spot) with a secure source: read from an environment variable (os.environ["API_KEY"] or similar) or prompt securely via getpass.getpass() and fail fast if missing. Update any other occurrences that print or expose api_key (the prints at the other mentioned ranges) to ensure no key, length, or preview is emitted.src/static/app.js (1)
74-79:⚠️ Potential issue | 🟠 MajorDo not persist API keys in localStorage by default.
Line 76 and Line 96 store/retrieve long-lived secrets from
localStoragewithout explicit opt-in. UsesessionStorageby default and gatelocalStoragebehind a user “Remember key” choice.Also applies to: 95-97
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/static/app.js` around lines 74 - 79, The code currently reads and writes long-lived API keys directly to localStorage; change the default to use sessionStorage for getItem/setItem in the DOMContentLoaded loader and in the save logic, and only write to localStorage when the user explicitly opts-in via a "Remember key" checkbox. Concretely: in the window.addEventListener('DOMContentLoaded', ...) loader and the counterpart save handler that references STORAGE_KEY and document.getElementById('apiKey'), switch to sessionStorage.getItem/sessionStorage.setItem by default and add a conditional that writes to localStorage only if a document.getElementById('rememberKey') checkbox is checked (otherwise clear localStorage entry if present). Ensure both load and save paths consistently honor the rememberKey option.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/social_media_backend.py`:
- Around line 41-110: The file fails CI because it is not formatted to Black's
expectations; run Black (e.g., black src/social_media_backend.py or black src
tests) to reformat the file, verify generate_post and its surrounding code are
reformatted (PEP8/Black style), stage and commit the changes, and push so the
pipeline's `black --check src tests` passes.
In `@src/static/index.html`:
- Line 36: The privacy hint text is inaccurate: the page currently claims the
API key is "never sent to our servers" but the client does POST the api_key to
the /generate-post endpoint; update the <small class="hint"> text to accurately
reflect handling (e.g., state that the key is sent to this server endpoint for
processing or removed/obfuscated client-side) so users aren’t misled, and ensure
the copy references the api_key submission to /generate-post or clarifies key
storage only occurs locally if that is true; locate the string in
src/static/index.html (the small.hint element) and replace it with the corrected
wording.
---
Duplicate comments:
In `@run_server.py`:
- Around line 8-10: Change the hardcoded uvicorn startup args to safer defaults
and make binding/reload opt-in: set host default to "127.0.0.1" instead of
"0.0.0.0" and keep reload disabled by default; read an environment variable
(e.g., START_PUBLIC or BIND_ALL) to allow binding to 0.0.0.0 and another env var
(e.g., ENABLE_RELOAD or DEV_RELOAD) to enable reload. Update the
uvicorn.run(...) call (the block that currently passes host="0.0.0.0",
port=8000, reload=True) to use os.getenv checks to choose host and reload
values, and document or name the env vars clearly so startup remains
localhost/no-reload unless explicitly opted-in.
In `@src/social_media_backend.py`:
- Line 68: The code calls response.json() directly when building error_msg
(e.g., the variable error_msg and the error-handling blocks around those
response usages), which will raise on non-JSON error bodies; instead, guard JSON
parsing by checking Content-Type for application/json or wrapping
response.json() in a try/except (catch ValueError/JSONDecodeError) and fall back
to response.text (or an empty string) before raising the HTTPException — update
both places that call response.json() so they safely parse or fallback and
include the safe message in the HTTPException.
- Around line 48-50: Remove the secret-derived API key preview and length
logging: delete the key_preview computation and the print statement that
references clean_api_key (the lines computing key_preview and the print(f"DEBUG:
Using API key...")). If you still need a trace, replace with a non-sensitive log
such as a single message like "DEBUG: API key provided" or no-op, but do not
include clean_api_key, key_preview, or its length anywhere in logs; update any
callers in this module that rely on key_preview accordingly.
In `@src/static/app.js`:
- Around line 74-79: The code currently reads and writes long-lived API keys
directly to localStorage; change the default to use sessionStorage for
getItem/setItem in the DOMContentLoaded loader and in the save logic, and only
write to localStorage when the user explicitly opts-in via a "Remember key"
checkbox. Concretely: in the window.addEventListener('DOMContentLoaded', ...)
loader and the counterpart save handler that references STORAGE_KEY and
document.getElementById('apiKey'), switch to
sessionStorage.getItem/sessionStorage.setItem by default and add a conditional
that writes to localStorage only if a document.getElementById('rememberKey')
checkbox is checked (otherwise clear localStorage entry if present). Ensure both
load and save paths consistently honor the rememberKey option.
In `@src/static/index.html`:
- Line 110: The external anchor link with href
"https://github.com/mitanuriel/agentSecOps_Guardian" that opens in a new tab
(target="_blank") should include rel="noopener noreferrer" to prevent
reverse-tabnabbing; update the <a> element for that GitHub link in
src/static/index.html to add rel="noopener noreferrer" while preserving the
existing target and link text.
In `@src/static/styles.css`:
- Around line 29-37: The global CSS rule hiding the cursor on the body (`body {
cursor: none; }`) is unsafe; remove that global `cursor: none` and instead add a
scoped rule that only hides the cursor when a JS-enabled flag class is present
(e.g. create a `.custom-cursor-enabled` selector that applies `cursor: none` to
`body` and any button selectors that previously relied on the global rule).
Then, in your cursor initialization code in app.js, after the cursor/follower
setup completes, add a call to add the flag class to the document body (use
`document.body.classList.add('custom-cursor-enabled')`) so the custom cursor is
only hidden if JS successfully initialized it.
In `@test_api_key.py`:
- Around line 12-15: Remove any CLI printing of API key metadata (the prints
that reference api_key, its length, and preview) so the test does not leak
secrets; instead validate without logging key content. Replace reading secrets
from argv (where sys.argv or argparse parses the secret at the noted spot) with
a secure source: read from an environment variable (os.environ["API_KEY"] or
similar) or prompt securely via getpass.getpass() and fail fast if missing.
Update any other occurrences that print or expose api_key (the prints at the
other mentioned ranges) to ensure no key, length, or preview is emitted.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
DASHBOARD.mdrun_server.pysrc/social_media_backend.pysrc/static/app.jssrc/static/index.htmlsrc/static/styles.csstest_api_key.py
| @app.post("/generate-post") | ||
| def generate_post(payload: GeneratePostRequest) -> dict: | ||
| """Generate a social media post from a user prompt using Mistral.""" | ||
| response = requests.post( | ||
| MISTRAL_CHAT_COMPLETIONS_URL, | ||
| headers={ | ||
| "Authorization": f"Bearer {payload.api_key}", | ||
| "Content-Type": "application/json", | ||
| }, | ||
| json={ | ||
| try: | ||
| # Clean the API key (remove any whitespace) | ||
| clean_api_key = payload.api_key.strip() | ||
|
|
||
| # Log key format for debugging (first/last 4 chars only) | ||
| key_preview = f"{clean_api_key[:4]}...{clean_api_key[-4:]}" if len(clean_api_key) > 8 else "***" | ||
| print(f"DEBUG: Using API key: {key_preview}, length: {len(clean_api_key)}") | ||
|
|
||
| response = requests.post( | ||
| MISTRAL_CHAT_COMPLETIONS_URL, | ||
| headers={ | ||
| "Authorization": f"Bearer {clean_api_key}", | ||
| "Content-Type": "application/json", | ||
| }, | ||
| json={ | ||
| "model": payload.model, | ||
| "messages": [{"role": "user", "content": payload.prompt}], | ||
| }, | ||
| timeout=60, | ||
| ) | ||
|
|
||
| # Handle API errors with helpful messages | ||
| if response.status_code == 401: | ||
| from fastapi import HTTPException | ||
| error_msg = response.json().get("message", "") if response.text else "" | ||
| print(f"DEBUG: 401 error details - {error_msg}") | ||
| raise HTTPException( | ||
| status_code=401, | ||
| detail=f"Invalid API key. Check: 1) Key is correctly copied from Mistral console 2) No extra spaces 3) Key is activated. API response: {error_msg}" | ||
| ) | ||
| elif response.status_code == 429: | ||
| from fastapi import HTTPException | ||
| raise HTTPException( | ||
| status_code=429, | ||
| detail="Rate limit exceeded. Please wait a moment and try again." | ||
| ) | ||
| elif response.status_code >= 400: | ||
| from fastapi import HTTPException | ||
| error_detail = response.json().get("error", {}).get("message", "Unknown error") | ||
| raise HTTPException( | ||
| status_code=response.status_code, | ||
| detail=f"Mistral API error: {error_detail}" | ||
| ) | ||
|
|
||
| response.raise_for_status() | ||
|
|
||
| completion = response.json() | ||
| post_text = completion["choices"][0]["message"]["content"] | ||
|
|
||
| return { | ||
| "post": post_text, | ||
| "model": payload.model, | ||
| "messages": [{"role": "user", "content": payload.prompt}], | ||
| }, | ||
| timeout=60, | ||
| ) | ||
| response.raise_for_status() | ||
|
|
||
| completion = response.json() | ||
| post_text = completion["choices"][0]["message"]["content"] | ||
|
|
||
| return { | ||
| "post": post_text, | ||
| "model": payload.model, | ||
| "provider": "mistral", | ||
| } | ||
| "provider": "mistral", | ||
| } | ||
|
|
||
| except requests.exceptions.Timeout: | ||
| from fastapi import HTTPException | ||
| raise HTTPException( | ||
| status_code=504, | ||
| detail="Request timeout. The API took too long to respond. Please try again." | ||
| ) | ||
| except requests.exceptions.RequestException as e: | ||
| from fastapi import HTTPException | ||
| raise HTTPException( | ||
| status_code=500, | ||
| detail=f"Network error: {str(e)}" | ||
| ) |
There was a problem hiding this comment.
CI is currently blocked by formatting in this file.
Pipeline reports black --check src tests failing with this file needing reformatting. Please run Black and commit the formatted output to unblock merge.
🧰 Tools
🪛 Ruff (0.15.2)
[warning] 93-97: Consider moving this statement to an else block
(TRY300)
[warning] 101-104: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
[warning] 107-110: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
[warning] 109-109: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/social_media_backend.py` around lines 41 - 110, The file fails CI because
it is not formatted to Black's expectations; run Black (e.g., black
src/social_media_backend.py or black src tests) to reformat the file, verify
generate_post and its surrounding code are reformatted (PEP8/Black style), stage
and commit the changes, and push so the pipeline's `black --check src tests`
passes.
| placeholder="Enter your Mistral API key" | ||
| required | ||
| > | ||
| <small class="hint">Your key is stored locally and never sent to our servers</small> |
There was a problem hiding this comment.
Fix inaccurate privacy wording for API key handling.
Line 36 says the key is “never sent to our servers,” but the client sends api_key to /generate-post. This is misleading for users and should be corrected.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/static/index.html` at line 36, The privacy hint text is inaccurate: the
page currently claims the API key is "never sent to our servers" but the client
does POST the api_key to the /generate-post endpoint; update the <small
class="hint"> text to accurately reflect handling (e.g., state that the key is
sent to this server endpoint for processing or removed/obfuscated client-side)
so users aren’t misled, and ensure the copy references the api_key submission to
/generate-post or clarifies key storage only occurs locally if that is true;
locate the string in src/static/index.html (the small.hint element) and replace
it with the corrected wording.
|
✅ Created PR with unit tests: #3 |
- Change run_server.py to bind to 127.0.0.1 by default (not 0.0.0.0) - Disable auto-reload by default in production - Add environment variables (BIND_HOST, BIND_PORT, BIND_RELOAD) for configuration - Remove API key exposure from test_api_key.py (no length/preview logging) - Replace command-line argument with secure getpass prompt - Read API key from MISTRAL_API_KEY environment variable or secure prompt - Prevent API keys from appearing in shell history or terminal output
Beautiful Lilac Theme
Magical Wizard Wand Cursor
Dashboard Functionality
Enhanced Error Handling
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Documentation
Tools