Skip to content

fix: resolve newline and semicolon safety filter bypasses#506

Open
SaumyaSngh323 wants to merge 4 commits into
rishabh0510rishabh:mainfrom
SaumyaSngh323:fix/safety-filter-bypass
Open

fix: resolve newline and semicolon safety filter bypasses#506
SaumyaSngh323 wants to merge 4 commits into
rishabh0510rishabh:mainfrom
SaumyaSngh323:fix/safety-filter-bypass

Conversation

@SaumyaSngh323

@SaumyaSngh323 SaumyaSngh323 commented Jun 3, 2026

Copy link
Copy Markdown

Description

Related Issues

Changes Made

Verification

  • Added unit tests
  • Ran pytest tests/ successfully
  • Manually tested via the API / CLI
  • (If applicable) Generated scripts pass SafetyFilter

Documentation

  • Updated docs/FEATURES.md (if adding a feature/profile)
  • Updated CHANGELOG.md
  • Code is fully documented and type-hinted

Screenshots (if applicable)

Closes #298

Summary by CodeRabbit

  • New Features

    • Administrators can now update user profiles through a new API endpoint
  • Chores

    • Improved detection mechanisms for malicious shell commands and dangerous execution patterns

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

@sauumya is attempting to deploy a commit to the rishabhmishra0510-5147's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@SaumyaSngh323, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 23 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89e53bd8-a4d1-4851-a899-fa120502227b

📥 Commits

Reviewing files that changed from the base of the PR and between 230676c and 8ad5cac.

📒 Files selected for processing (2)
  • backend/app/api/v1/profiles.py
  • backend/app/templates/safety.py
📝 Walkthrough

Walkthrough

Two independent changes: a new PATCH endpoint for admin-authenticated profile updates in the API, and expanded regex patterns in the safety validator to detect additional shell injection, command-execution, and data-destruction variants.

Changes

Profile Update Endpoint

Layer / File(s) Summary
PATCH endpoint for profile updates
backend/app/api/v1/profiles.py
Imports ProfileUpdateSchema and adds an async update_profile handler that accepts admin-authenticated requests with a partial profile payload, invokes the profile service, raises EntityNotFoundError with PROFILE_NOT_FOUND when no update occurs, and returns the updated profile as ProfileDetailSchema.

Enhanced Safety Pattern Detection

Layer / File(s) Summary
Forbidden command pattern detection
backend/app/templates/safety.py
Updates FORBIDDEN_PATTERNS with refined regexes to block fork-bomb, disk write, shutdown/reboot, SQL destruction, curl/wget download-and-execute pipelines (including output/redirect and chained variants), PowerShell cradles, and base64-to-shell execution. Compilation logic remains unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #298: Fixes the semicolon/newline bypass in wget download-and-execute patterns by expanding the regex to match multiple command separators and alternate pipeline forms.

Possibly related PRs

Suggested labels

level:intermediate, gssoc:approved, level2, NSoC'26

Suggested reviewers

  • rishabh0510rishabh

Poem

🐰 A profile endpoint blooms so fine,
While safety gates grow more divine,
From wget tricks to shells concealed,
With regex shields the risks are sealed.
A rabbit cheers this guard so keen! 🛡️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes an unrelated PATCH /profiles/{slug} endpoint addition in backend/app/api/v1/profiles.py that is not mentioned in the linked issue #298 and appears to be out of scope for the safety filter fix. Remove the ProfileUpdateSchema and update_profile endpoint changes from this PR, as they relate to issue #297 (profile updates) and should be in a separate pull request focused solely on the safety filter fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: resolve newline and semicolon safety filter bypasses' directly addresses the main objective of fixing safety filter bypasses related to download-and-execute patterns with semicolon and newline separators, which is the primary focus of the PR.
Linked Issues check ✅ Passed The PR addresses issue #298 by updating FORBIDDEN_PATTERNS regex in backend/app/templates/safety.py to detect download-and-execute patterns using multiple separators (semicolons, newlines) instead of only &&, meeting the stated objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🔍 PR Action Required

Hi @SaumyaSngh323,

We detected some items on this Pull Request that require attention:

❌ Merge Conflicts

This branch has merge conflicts with the target branch. Please resolve the conflicts.

❌ Failing CI Checks

The following check runs or commit statuses are failing (ignoring vercel):

Please resolve the issues above to proceed.


Last updated: Thu, 04 Jun 2026 12:54:53 GMT

@SaumyaSngh323 SaumyaSngh323 force-pushed the fix/safety-filter-bypass branch from 230676c to ef9843b Compare June 3, 2026 16:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
backend/app/api/v1/profiles.py (2)

208-212: ⚡ Quick win

Consider adding rate limiting for consistency with create endpoint.

The create_profile endpoint at line 148 applies general_rate_limit to prevent abuse of admin write operations. For consistency and defense-in-depth, consider applying the same rate limiting to this PATCH endpoint.

🔒 Proposed addition: add rate limit dependency
 async def update_profile(
     slug: str,
     profile_update: ProfileUpdateSchema,
     db: DB,
+    _rate_limit: None = Depends(general_rate_limit),
     _auth: None = Depends(require_admin),
 ) -> ProfileDetailSchema:
🤖 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/app/api/v1/profiles.py` around lines 208 - 212, The PATCH endpoint
update_profile should apply the same general_rate_limit dependency as
create_profile to protect admin write operations; update the update_profile
function signature to accept a new parameter (e.g., _rate_limit: None =
Depends(general_rate_limit)) alongside _auth (or next to it) so the rate-limit
dependency is enforced, and ensure general_rate_limit is imported where used.

202-210: ⚡ Quick win

Enhance API documentation with responses, description, and Path validation.

For consistency with other endpoints in this module, consider adding:

  1. A responses dictionary documenting status codes (201, 401, 404, 422, 503)
  2. A description parameter explaining partial update semantics
  3. Path(...) validation for the slug parameter with description and examples
📝 Proposed enhancement: add complete API documentation
 `@router.patch`(
     "/profiles/{slug}",
     response_model=ProfileDetailSchema,
     summary="Partially update an environment profile",
+    description=(
+        "Partially update an existing environment profile. Only provided fields "
+        "will be updated; omitted fields remain unchanged."
+    ),
     tags=["Profiles"],
+    responses={
+        200: {"description": "Profile updated successfully"},
+        401: {"description": "Missing or invalid admin API key"},
+        404: {"description": "Profile not found"},
+        422: {"description": "Request validation error"},
+        503: {"description": "Admin API key not configured on this server"},
+    },
 )
 async def update_profile(
-    slug: str,
+    slug: str = Path(
+        ...,
+        description="Unique slug of the environment profile to update.",
+        examples=["pytorch-cu121"],
+    ),
     profile_update: ProfileUpdateSchema,
     db: DB,
     _auth: None = Depends(require_admin),
 ) -> ProfileDetailSchema:
🤖 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/app/api/v1/profiles.py` around lines 202 - 210, Update the
router.patch decorator for update_profile to include a responses dictionary
documenting 201, 401, 404, 422, and 503 status codes, add a description
explaining that this endpoint performs a partial update (only supplied fields
are modified), and validate the slug path parameter by changing the function
signature to use Path(...) with a short description and examples; reference the
update_profile function and the slug parameter so you add these metadata to the
existing decorator (keeping response_model=ProfileDetailSchema and
tags=["Profiles"]).
🤖 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/app/api/v1/profiles.py`:
- Around line 218-224: The call to profile_service.update_profile can raise DB
exceptions and needs the same try/except handling used in create_profile: wrap
the profile = await profile_service.update_profile(db, slug, profile_update)
call in a try block and catch sqlalchemy.exc.IntegrityError and
sqlalchemy.exc.SQLAlchemyError; on IntegrityError re-raise the same or an
InvalidRequestError with a clear message and on SQLAlchemyError log/raise a
generic server error, then continue to raise EntityNotFoundError when profile is
falsy before returning ProfileDetailSchema.model_validate(profile). Ensure you
reference profile_service.update_profile, EntityNotFoundError, and
ProfileDetailSchema.model_validate when making the changes.
- Around line 202-224: The update_profile endpoint is incorrectly indented
inside the delete_profile function, preventing FastAPI from registering the
route; move the whole async def update_profile(...) block (including the
`@router.patch`(...) decorator and its body that references
profile_service.update_profile and ProfileDetailSchema.model_validate) out to
module-level (dedent so the def starts in column 0) so it is not nested inside
delete_profile and can be registered by the router.

In `@backend/app/templates/safety.py`:
- Line 29: The module crashes at import because invalid regexes in
FORBIDDEN_PATTERNS are compiled into _COMPILED; fix the bad patterns and make
compilation safe: correct the shutdown pattern (replace the invalid
r"shutdown\s+(/[s/r|-h|-r)" with a valid grouping such as a non-capturing
alternation like r"shutdown\s+/(?:s|r|h|-h|-r)" or whatever specific switches
are intended) and fix the PowerShell cradle pattern(s) around the block noted
near lines 67–69 so parentheses and escapes are balanced; after fixing patterns,
either validate them with re.compile locally or move the _COMPILED creation into
a lazy initializer (e.g., a function that compiles on first use) so import-time
compilation cannot raise. Ensure you update references to _COMPILED and the
specific pattern strings in FORBIDDEN_PATTERNS accordingly.
- Line 71: The regex on line containing
r"(?:iex|Invoke-Expression)\s*\|\s*(?:\?s*(?:\(?\:New-Object\)\s+Net\.WebClient\)?)\.(?:DownloadString|DownloadFile))\s*\("
is malformed and only matches cases with a pipe; replace it with a corrected
pattern that allows either a direct call or a piped one and fixes the incorrect
escapes — e.g. use
r"(?:iex|Invoke-Expression)(?:\s*\|\s*)?\s*\(?New-Object\s+Net\.WebClient\)?\.(?:DownloadString|DownloadFile)\s*\("
to match both "iex (New-Object Net.WebClient).DownloadString(...)" and "iex |
(New-Object Net.WebClient).DownloadString(...)" in the safety.py regex list.
- Line 26: The fork‑bomb regex tuple in backend/app/templates/safety.py
currently requires the '|' immediately after '{', so it misses the canonical
payload ":(){:|:&};:". Update that tuple's pattern to allow optional
characters (like ':' or whitespace) between '{' and '|' and to match either '&'
or HTML-escaped '&' before the final ';', and allow optional whitespace
around tokens; in short, replace the existing pattern with one that matches the
canonical payload form (e.g. patterns that permit characters after '{', a '|'
later, and either '&' or '&' before ';') while keeping the same "Fork bomb
pattern" label.
- Line 75: The current regex tuple in safety.py uses the pattern
"base64\s+--decode\s*\|\s*. *sh" which fails to match compact forms like
"--decode|sh" or "--decode| bash" and incorrectly requires an extra
any-character before "sh"; update that tuple's regex to allow an optional
literal dot (to match "./sh" variants), accept no-space compact pipes, and match
both "sh" and "bash" using an optional "ba" and a word boundary so it catches
"sh" and "bash" without overmatching.

---

Nitpick comments:
In `@backend/app/api/v1/profiles.py`:
- Around line 208-212: The PATCH endpoint update_profile should apply the same
general_rate_limit dependency as create_profile to protect admin write
operations; update the update_profile function signature to accept a new
parameter (e.g., _rate_limit: None = Depends(general_rate_limit)) alongside
_auth (or next to it) so the rate-limit dependency is enforced, and ensure
general_rate_limit is imported where used.
- Around line 202-210: Update the router.patch decorator for update_profile to
include a responses dictionary documenting 201, 401, 404, 422, and 503 status
codes, add a description explaining that this endpoint performs a partial update
(only supplied fields are modified), and validate the slug path parameter by
changing the function signature to use Path(...) with a short description and
examples; reference the update_profile function and the slug parameter so you
add these metadata to the existing decorator (keeping
response_model=ProfileDetailSchema and tags=["Profiles"]).
🪄 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

Run ID: 3651c274-2625-4a36-9e9f-958a8eaf2daa

📥 Commits

Reviewing files that changed from the base of the PR and between de1183f and 230676c.

📒 Files selected for processing (2)
  • backend/app/api/v1/profiles.py
  • backend/app/templates/safety.py

Comment thread backend/app/api/v1/profiles.py
Comment on lines +218 to +224
profile = await profile_service.update_profile(db, slug, profile_update)
if not profile:
raise EntityNotFoundError(
resource=f"Profile '{slug}'",
error_code="PROFILE_NOT_FOUND",
)
return ProfileDetailSchema.model_validate(profile)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add database error handling to prevent unhandled exceptions.

The profile_service.update_profile call may raise database exceptions (IntegrityError, SQLAlchemyError) that would propagate unhandled. The create_profile endpoint at lines 155-166 demonstrates the proper pattern with try-except blocks for these exceptions.

🛡️ Proposed fix: wrap service call in try-except
     logger.info("Admin write: updating profile slug=%s", slug)
-    profile = await profile_service.update_profile(db, slug, profile_update)
-    if not profile:
-        raise EntityNotFoundError(
-            resource=f"Profile '{slug}'",
-            error_code="PROFILE_NOT_FOUND",
-        )
-    return ProfileDetailSchema.model_validate(profile)
+    try:
+        profile = await profile_service.update_profile(db, slug, profile_update)
+        if not profile:
+            raise EntityNotFoundError(
+                resource=f"Profile '{slug}'",
+                error_code="PROFILE_NOT_FOUND",
+            )
+        logger.info("Profile updated: slug=%s", slug)
+        return ProfileDetailSchema.model_validate(profile)
+    except IntegrityError as exc:
+        await db.rollback()
+        logger.warning("Integrity error updating profile slug=%s", slug)
+        raise ConflictError(f"Update violates constraints for profile '{slug}'.") from exc
+    except SQLAlchemyError as exc:
+        await db.rollback()
+        logger.exception("Database error while updating profile")
+        raise InternalServerError("Failed to update profile.") from exc
📝 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.

Suggested change
profile = await profile_service.update_profile(db, slug, profile_update)
if not profile:
raise EntityNotFoundError(
resource=f"Profile '{slug}'",
error_code="PROFILE_NOT_FOUND",
)
return ProfileDetailSchema.model_validate(profile)
try:
profile = await profile_service.update_profile(db, slug, profile_update)
if not profile:
raise EntityNotFoundError(
resource=f"Profile '{slug}'",
error_code="PROFILE_NOT_FOUND",
)
logger.info("Profile updated: slug=%s", slug)
return ProfileDetailSchema.model_validate(profile)
except IntegrityError as exc:
await db.rollback()
logger.warning("Integrity error updating profile slug=%s", slug)
raise ConflictError(f"Update violates constraints for profile '{slug}'.") from exc
except SQLAlchemyError as exc:
await db.rollback()
logger.exception("Database error while updating profile")
raise InternalServerError("Failed to update profile.") from exc
🤖 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/app/api/v1/profiles.py` around lines 218 - 224, The call to
profile_service.update_profile can raise DB exceptions and needs the same
try/except handling used in create_profile: wrap the profile = await
profile_service.update_profile(db, slug, profile_update) call in a try block and
catch sqlalchemy.exc.IntegrityError and sqlalchemy.exc.SQLAlchemyError; on
IntegrityError re-raise the same or an InvalidRequestError with a clear message
and on SQLAlchemyError log/raise a generic server error, then continue to raise
EntityNotFoundError when profile is falsy before returning
ProfileDetailSchema.model_validate(profile). Ensure you reference
profile_service.update_profile, EntityNotFoundError, and
ProfileDetailSchema.model_validate when making the changes.

Comment thread backend/app/templates/safety.py
(r"dd\s+if=", "Raw disk write command"),
(r">\s*/dev/sd[a-z]", "Direct disk write"),
(r"shutdown\s+(/s|/r|-h|-r)", "System shutdown/reboot"),
(r"shutdown\s+(/[s/r|-h|-r)", "System shutdown/reboot"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import ast, re, pathlib
p = pathlib.Path("backend/app/templates/safety.py")
tree = ast.parse(p.read_text())
patterns = None
for n in tree.body:
    if isinstance(n, ast.AnnAssign) and isinstance(n.target, ast.Name) and n.target.id == "FORBIDDEN_PATTERNS":
        patterns = n.value.elts
        break
if patterns is None:
    raise SystemExit("FORBIDDEN_PATTERNS not found")
for i, elt in enumerate(patterns, 1):
    pattern = ast.literal_eval(elt.elts[0])
    desc = ast.literal_eval(elt.elts[1])
    try:
        re.compile(pattern, re.IGNORECASE | re.DOTALL)
    except re.error as e:
        print(f"{i}. {desc}: {e}")
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 230


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Print relevant portions of the file with line numbers (around the reported line ranges)
echo "=== backend/app/templates/safety.py: lines 1-120 (for mapping) ==="
nl -ba backend/app/templates/safety.py | sed -n '1,120p'

# 2) Enumerate FORBIDDEN_PATTERNS: index, desc, pattern literal, compile status
python3 - <<'PY'
import ast, re, pathlib, textwrap

p = pathlib.Path("backend/app/templates/safety.py")
tree = ast.parse(p.read_text())

patterns = None
for n in tree.body:
    if isinstance(n, ast.AnnAssign) and isinstance(n.target, ast.Name) and n.target.id == "FORBIDDEN_PATTERNS":
        patterns = n.value.elts
        break
if patterns is None:
    raise SystemExit("FORBIDDEN_PATTERNS not found")

fails = []
for i, elt in enumerate(patterns, 1):
    pattern = ast.literal_eval(elt.elts[0])
    desc = ast.literal_eval(elt.elts[1])
    try:
        re.compile(pattern, re.IGNORECASE | re.DOTALL)
        ok = True
        err = None
    except re.error as e:
        ok = False
        err = str(e)
        fails.append((i, desc, err, pattern))
    print(f"{i:02d} | {desc}\n    pattern={pattern}\n    compile={'OK' if ok else 'FAIL: '+err}\n")

print("\n=== Failures ===")
for i, desc, err, pattern in fails:
    print(f"{i}. {desc}: {err}")

# 3) Compile the proposed replacement regexes from the suggested patch
print("\n=== Proposed replacement regex compilation ===")
replacements = {
    "shutdown_repl": r"shutdown\s+(?:/(?:s|r)|-(?:h|r))\b",
    "powershell_repl": r"(?:iex|Invoke-Expression)\s*\(\s*(?:iwr|Invoke-WebRequest|Invoke-RestMethod|irm|curl|wget)\s+(?!https?://astral\.sh/)\S+.*\)",
}
for name, pat in replacements.items():
    try:
        re.compile(pat, re.IGNORECASE | re.DOTALL)
        print(f"{name}: OK")
    except re.error as e:
        print(f"{name}: FAIL: {e}")
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== backend/app/templates/safety.py: show around FORBIDDEN_PATTERNS ==="
awk 'NR>=1 && NR<=140 {printf "%5d:%s\n", NR, $0}' backend/app/templates/safety.py | sed -n '1,140p'

echo
echo "=== Find exact line ranges containing the failing descriptors ==="
rg -n "System shutdown/reboot|PowerShell malicious download cradle" backend/app/templates/safety.py || true

echo
echo "=== Enumerate FORBIDDEN_PATTERNS: index, desc, pattern, compile status ==="
python3 - <<'PY'
import ast, re, pathlib
p = pathlib.Path("backend/app/templates/safety.py")
tree = ast.parse(p.read_text())

patterns = None
for n in tree.body:
    if isinstance(n, ast.AnnAssign) and isinstance(n.target, ast.Name) and n.target.id == "FORBIDDEN_PATTERNS":
        patterns = n.value.elts
        break
if patterns is None:
    raise SystemExit("FORBIDDEN_PATTERNS not found")

fails = []
for i, elt in enumerate(patterns, 1):
    pattern = ast.literal_eval(elt.elts[0])
    desc = ast.literal_eval(elt.elts[1])
    try:
        re.compile(pattern, re.IGNORECASE | re.DOTALL)
        status = "OK"
    except re.error as e:
        status = f"FAIL: {e}"
        fails.append((i, desc, status, pattern))
    print(f"{i:02d} | {desc} | {status}")

print("\nFailures:")
for i, desc, status, pattern in fails:
    print(f"{i}. {desc}: {status}\n   pattern={pattern}")
PY

echo
echo "=== Compile the proposed replacement regexes ==="
python3 - <<'PY'
import re
repls = [
("shutdown_repl", r"shutdown\s+(?:/(?:s|r)|-(?:h|r))\b"),
("powershell_repl", r"(?:iex|Invoke-Expression)\s*\(\s*(?:iwr|Invoke-WebRequest|Invoke-RestMethod|irm|curl|wget)\s+(?!https?://astral\.sh/)\S+.*\)"),
]
for name, pat in repls:
    try:
        re.compile(pat, re.IGNORECASE | re.DOTALL)
        print(f"{name}: OK")
    except re.error as e:
        print(f"{name}: FAIL: {e}")
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 6744


Fix import-time re compilation failures in backend/app/templates/safety.py (shutdown + PowerShell cradle).

_COMPILED compiles FORBIDDEN_PATTERNS at import time; currently the module crashes before any safety enforcement:

  • Line 34: r"shutdown\s+(/[s/r|-h|-r)"re.error: bad character range |-h
  • Lines 67-69: PowerShell malicious download cradlere.error: missing ), unterminated subpattern
Suggested patch
-    (r"shutdown\s+(/[s/r|-h|-r)", "System shutdown/reboot"),
+    (r"shutdown\s+(?:/(?:s|r)|-(?:h|r))\b", "System shutdown/reboot"),
@@
-        r"(?:iex|Invoke-Expression)\s*\|\s*(?:\?s*(?:iwr|Invoke-WebRequest|Invoke-RestMethod|irm|curl|wget)\s+(?!https?://astral\.sh/)\S+.*"
-        r"|Invoke-Expression\s+.*?(?:iex|Invoke-Expression)",
+        r"(?:iex|Invoke-Expression)\s*\(\s*(?:iwr|Invoke-WebRequest|Invoke-RestMethod|irm|curl|wget)\s+(?!https?://astral\.sh/)\S+.*\)",
         "PowerShell malicious download cradle",
📝 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.

Suggested change
(r"shutdown\s+(/[s/r|-h|-r)", "System shutdown/reboot"),
(r"shutdown\s+(?:/(?:s|r)|-(?:h|r))\b", "System shutdown/reboot"),
🤖 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/app/templates/safety.py` at line 29, The module crashes at import
because invalid regexes in FORBIDDEN_PATTERNS are compiled into _COMPILED; fix
the bad patterns and make compilation safe: correct the shutdown pattern
(replace the invalid r"shutdown\s+(/[s/r|-h|-r)" with a valid grouping such as a
non-capturing alternation like r"shutdown\s+/(?:s|r|h|-h|-r)" or whatever
specific switches are intended) and fix the PowerShell cradle pattern(s) around
the block noted near lines 67–69 so parentheses and escapes are balanced; after
fixing patterns, either validate them with re.compile locally or move the
_COMPILED creation into a lazy initializer (e.g., a function that compiles on
first use) so import-time compilation cannot raise. Ensure you update references
to _COMPILED and the specific pattern strings in FORBIDDEN_PATTERNS accordingly.

Comment thread backend/app/templates/safety.py
),
(r"eval\s+\$\(", "Eval of subshell output"),
(r"base64\s+--decode\s*\|.*sh", "Base64 decode pipe to shell"),
(r"base64\s+--decode\s*\|\s*. *sh", "Base64 decode pipe to shell"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import re
pattern = r"base64\s+--decode\s*\|\s*. *sh"
samples = [
    "echo ZWNobyBoaQ== | base64 --decode | sh",
    "echo ZWNobyBoaQ== | base64 --decode|sh",
]
for s in samples:
    print(bool(re.search(pattern, s, re.IGNORECASE | re.DOTALL)), " :: ", s)
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 173


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant snippet around the referenced line
echo "---- backend/app/templates/safety.py (around line 60-90) ----"
nl -ba backend/app/templates/safety.py | sed -n '55,95p'

# Run regex comparisons between current and proposed patterns on multiple samples
echo "---- regex match comparison ----"
python3 - <<'PY'
import re

current = r"base64\s+--decode\s*\|\s*. *sh"
proposed = r"base64\s+--decode\s*\|\s*(?:ba)?sh\b"

samples = [
  "echo ZWNoby | base64 --decode | sh",
  "echo ZWNoby | base64 --decode|sh",
  "echo ZWNoby | base64 --decode |bash",
  "echo ZWNoby | base64 --decode| bash",
  "echo ZWNoby | base64 --decode | bsh",     # odd
  "echo ZWNoby | base64 --decode | bash",
  "echo ZWNoby | base64 --decode | bash -i",
  "echo ZWNoby | BASE64 --DECODE | SH",
  "echo ZWNoby | base64 --decode | sh -c 'id'",
  "echo ZWNoby | base64 --decode | . sh",    # current wildcard intends this
  "echo ZWNoby | base64 --decode | .sh",     # compact dot
  "echo ZWNoby | base64 --decode | .   sh", # spaced dot
  "echo ZWNoby | base64 --decode | ./sh",   # path
  "echo ZWNoby | base64 --decode | . ./sh", # odd
  "echo ZWNoby | base64 --decode | bash",
]

flags = re.IGNORECASE | re.DOTALL
print(f"{'sample':55} | current | proposed")
print("-"*95)
for s in samples:
  c = bool(re.search(current, s, flags))
  p = bool(re.search(proposed, s, flags))
  # shorten for display
  short = (s[:52] + "…") if len(s)>55 else s
  print(f"{short:55} | {str(c):7} | {str(p):8}")
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 195


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant snippet around the referenced line
echo "---- backend/app/templates/safety.py (around line 60-90) ----"
nl -ba backend/app/templates/safety.py | sed -n '55,95p'

# Run regex comparisons between current and proposed patterns on multiple samples
echo "---- regex match comparison ----"
python3 - <<'PY'
import re

current = r"base64\s+--decode\s*\|\s*. *sh"
proposed = r"base64\s+--decode\s*\|\s*(?:ba)?sh\b"

samples = [
  "echo ZWNoby | base64 --decode | sh",
  "echo ZWNoby | base64 --decode|sh",
  "echo ZWNoby | base64 --decode |bash",
  "echo ZWNoby | base64 --decode| bash",
  "echo ZWNoby | base64 --decode | bsh",     # odd
  "echo ZWNoby | base64 --decode | bash",
  "echo ZWNoby | base64 --decode | bash -i",
  "echo ZWNoby | BASE64 --DECODE | SH",
  "echo ZWNoby | base64 --decode | sh -c 'id'",
  "echo ZWNoby | base64 --decode | . sh",    # current wildcard intends this
  "echo ZWNoby | base64 --decode | .sh",     # compact dot
  "echo ZWNoby | base64 --decode | .   sh", # spaced dot
  "echo ZWNoby | base64 --decode | ./sh",   # path
  "echo ZWNoby | base64 --decode | . ./sh", # odd
  "echo ZWNoby | base64 --decode | bash",
]

flags = re.IGNORECASE | re.DOTALL
print(f"{'sample':55} | current | proposed")
print("-"*95)
for s in samples:
  c = bool(re.search(current, s, flags))
  p = bool(re.search(proposed, s, flags))
  short = (s[:52] + "…") if len(s)>55 else s
  print(f"{short:55} | {str(c):7} | {str(p):8}")
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 195


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- file stats ----"
wc -l backend/app/templates/safety.py || true

echo "---- backend/app/templates/safety.py (lines 60-90) ----"
python3 - <<'PY'
path = "backend/app/templates/safety.py"
start, end = 55, 95
with open(path, "r", encoding="utf-8") as f:
    lines = f.readlines()
for i in range(start, min(end, len(lines))):
    print(f"{i+1:4d}: {lines[i].rstrip()}")
PY

echo "---- regex match comparison (current vs proposed) ----"
python3 - <<'PY'
import re

current = r"base64\s+--decode\s*\|\s*. *sh"
proposed = r"base64\s+--decode\s*\|\s*(?:ba)?sh\b"

samples = [
  "echo ZWNoby | base64 --decode | sh",
  "echo ZWNoby | base64 --decode|sh",
  "echo ZWNoby | base64 --decode |bash",
  "echo ZWNoby | base64 --decode| bash",
  "echo ZWNoby | base64 --decode | bsh",     # odd
  "echo ZWNoby | base64 --decode | bash",
  "echo ZWNoby | base64 --decode | bash -i",
  "echo ZWNoby | BASE64 --DECODE | SH",
  "echo ZWNoby | base64 --decode | sh -c 'id'",
  "echo ZWNoby | base64 --decode | . sh",
  "echo ZWNoby | base64 --decode | .sh",
  "echo ZWNoby | base64 --decode | .   sh",
  "echo ZWNoby | base64 --decode | ./sh",
  "echo ZWNoby | base64 --decode | . ./sh",
  "echo ZWNoby | base64 --decode | bash",
  # additional whitespace edge cases
  "echo ZWNoby | base64 --decode  |sh",
  "echo ZWNoby | base64 --decode\t|sh",
]

flags = re.IGNORECASE | re.DOTALL
print(f"{'sample':70} | current | proposed")
print("-"*125)
for s in samples:
    c = bool(re.search(current, s, flags))
    p = bool(re.search(proposed, s, flags))
    short = (s[:67] + "…") if len(s) > 70 else s
    print(f"{short:70} | {str(c):7} | {str(p):9}")
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 3749


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import re

current = r"base64\s+--decode\s*\|\s*. *sh"
cand1 = r"base64\s+--decode\s*\|\s*(?:\.\s*)?(?:ba)?sh\b"  # preserves `.sh`/`. sh`, fixes `|sh`/`|bash`
cand2 = r"base64\s+--decode\s*\|\s*(?:\.\s*)?(?:ba)?sh\b.*"  # sanity (less strict)

samples = [
  "echo ZWNoby | base64 --decode | sh",
  "echo ZWNoby | base64 --decode|sh",
  "echo ZWNoby | base64 --decode |bash",
  "echo ZWNoby | base64 --decode| bash",
  "echo ZWNoby | base64 --decode | bsh",
  "echo ZWNoby | base64 --decode | bash",
  "echo ZWNoby | base64 --decode | bash -i",
  "echo ZWNoby | BASE64 --DECODE | SH",
  "echo ZWNoby | base64 --decode | sh -c 'id'",
  "echo ZWNoby | base64 --decode | . sh",
  "echo ZWNoby | base64 --decode | .sh",
  "echo ZWNoby | base64 --decode | .   sh",
  "echo ZWNoby | base64 --decode | ./sh",
]

flags = re.IGNORECASE | re.DOTALL
patterns = [("current", current), ("cand1", cand1), ("cand2", cand2)]
print(f"{'sample':75} | current | cand1 | cand2")
print("-"*110)
for s in samples:
  row = [s[:72] + "…" if len(s) > 75 else s]
  for _, pat in patterns:
    row.append(str(bool(re.search(pat, s, flags))).ljust(7))
  print(f"{row[0]:75} | " + " | ".join(row[1:]))
PY

Repository: rishabh0510rishabh/EnvForage

Length of output: 1663


Fix Base64-to-shell regex to catch compact --decode|sh / --decode|bash without breaking .sh variants.

backend/app/templates/safety.py’s r"base64\s+--decode\s*\|\s*. *sh" misses ... | base64 --decode|sh and ... | base64 --decode| bash-style compact pipes, because it expects a whitespace plus an extra character via . before sh. Replace it with an optional literal dot:

(r"base64\s+--decode\s*\|\s*(?:\.\s*)?(?:ba)?sh\b", "Base64 decode pipe to shell"),
🤖 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/app/templates/safety.py` at line 75, The current regex tuple in
safety.py uses the pattern "base64\s+--decode\s*\|\s*. *sh" which fails to match
compact forms like "--decode|sh" or "--decode| bash" and incorrectly requires an
extra any-character before "sh"; update that tuple's regex to allow an optional
literal dot (to match "./sh" variants), accept no-space compact pipes, and match
both "sh" and "bash" using an optional "ba" and a word boundary so it catches
"sh" and "bash" without overmatching.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 53 minutes and 46 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 53 minutes and 13 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 52 minutes and 50 seconds before sending another message.

@SaumyaSngh323

Copy link
Copy Markdown
Author

Hi @rishabh0510rishabh I have successfully force-pushed the clean single-commit fix and resolved the conflicts. Could you please check the implementation and restore the GSSoC and SSoC labels? Thank you!

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 51 minutes and 45 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 51 minutes and 26 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 51 minutes and 6 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 50 minutes and 46 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 50 minutes and 22 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 50 minutes and 3 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 49 minutes and 44 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 49 minutes and 26 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 49 minutes and 17 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 49 minutes and 5 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 58 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 55 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 51 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 47 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 41 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 38 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 32 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 28 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 23 seconds before sending another message.

1 similar comment
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 23 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 13 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 10 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 2 seconds before sending another message.

1 similar comment
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 26 minutes and 2 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 25 minutes and 56 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 25 minutes and 48 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 25 minutes and 40 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 25 minutes and 38 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 25 minutes and 37 seconds before sending another message.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@github-actions[bot] have exceeded the limit for the number of chat messages per hour. Please wait 25 minutes and 32 seconds before sending another message.

@rishabh0510rishabh

Copy link
Copy Markdown
Owner

hey @SaumyaSngh323 please remove merge conflicts and check for backend currently they are failing fix them too

@rishabh0510rishabh

Copy link
Copy Markdown
Owner

hey @SaumyaSngh323 there are merge conflicts that must be resolved and the backend tests are failing plese fix them

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Semicolon/newline bypass in wget download-and-execute safety filter pattern

3 participants