Skip to content

fix(bugs): prevent unauthenticated mass-assignment in POST /bugs#42

Open
Kunal241207 wants to merge 4 commits intoOWASP-BLT:mainfrom
Kunal241207:fix-bugs-mass-assignment
Open

fix(bugs): prevent unauthenticated mass-assignment in POST /bugs#42
Kunal241207 wants to merge 4 commits intoOWASP-BLT:mainfrom
Kunal241207:fix-bugs-mass-assignment

Conversation

@Kunal241207
Copy link
Copy Markdown
Contributor

@Kunal241207 Kunal241207 commented Mar 8, 2026

Summary

This PR fixes a security issue in POST /bugs where clients could set
server-managed fields directly via the request body.

Affected fields:

  • verified
  • user
  • closed_by
  • reporter_ip_address

Requests containing these fields are now rejected with HTTP 400 and the
values are controlled exclusively by the server.

A regression test has been added to ensure these fields cannot be set
via the API.

Summary by CodeRabbit

  • Bug Fixes

    • Enforced server-controlled fields on bug creation; extraneous client fields are rejected with 400 and a list of forbidden fields.
    • Added request-body sanitization and stricter URL/description validation (trim, non-empty, length, scheme, domain).
    • Server now captures reporter IP and resolves user identity; inserts use sanitized and server-derived values with sensible defaults.
  • Tests

    • Added security tests for forbidden-field rejection, URL/description validation, and successful creation with server-managed defaults.

@owasp-blt
Copy link
Copy Markdown

owasp-blt bot commented Mar 8, 2026

👋 Thanks for opening this pull request, @Kunal241207!

Before your PR is reviewed, please ensure:

  • Your code follows the project's coding style and guidelines.
  • You have written or updated tests for your changes.
  • The commit messages are clear and descriptive.
  • You have linked any relevant issues (e.g., Closes #123).

🔍 Our team will review your PR shortly. If you have questions, feel free to ask in the comments.

🚀 Keep up the great work! — OWASP BLT

@owasp-blt
Copy link
Copy Markdown

owasp-blt bot commented Mar 8, 2026

📊 Monthly Leaderboard

Hi @Kunal241207! Here's how you rank for March 2026:

Rank User Open PRs PRs (merged) PRs (closed) Reviews Comments Total
🥈 #2 @ojaswa072 10 29 0 0 1 302
🥉 #3 @Kunal241207 5 24 0 2 4 263
#4 @e-esakman 1 25 0 2 0 261

Scoring this month (across OWASP-BLT org): Open PRs (+1 each), Merged PRs (+10), Closed (not merged) (−2), Reviews (+5; first two per PR in-month), Comments (+2, excludes CodeRabbit). Run /leaderboard on any issue or PR to see your rank!

@owasp-blt
Copy link
Copy Markdown

owasp-blt bot commented Mar 8, 2026

👋 Hi @Kunal241207!

This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:

  • The PR author
  • coderabbitai
  • copilot

Once a valid peer review is submitted, this check will pass automatically. Thank you!

⚠️ Peer review enforcement is active.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 8, 2026

Walkthrough

Sanitized POST body using a positive allowlist; validated and normalized url; derived reporter_ip from CF-Connecting-IP and user_id from request; reject extraneous fields (400); INSERT uses sanitized values plus server-managed defaults; tests added for validation and server-side overrides.

Changes

Cohort / File(s) Summary
Bug handler: input sanitization & server-derived metadata
src/handlers/bugs.py
Add positive allowlist and build sanitized_body; reject extraneous fields (400). Validate/trim url (string, non-empty, <=200 chars, http/https, domain required). Enforce description as non-empty trimmed string. Extract reporter_ip from CF-Connecting-IP; derive user_id from request (dict-like or object). Use sanitized fields and server-derived metadata in INSERT; handle last_insert_rowid and fetch created record; many fields default to NULL or server values.
Security tests: input validation & server-enforced defaults
tests/test_bugs_security.py
New async test module with MockRequest, MockEnv, FakeDB and monkeypatched DB access. Tests cover rejection of forbidden fields (400 + error listing), acceptance of allowlisted fields with 201 and captured insert params, and validation errors for non-string or blank url. Validates server-applied defaults (e.g., views, verified, score, status, is_hidden, rewarded, reporter_ip_address, domain, user, closed_by).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Handler as Bug Handler
    participant Sanitizer as Sanitizer/Validator
    participant DB as Database

    Client->>Handler: POST /bugs (body, headers)
    Handler->>Sanitizer: Apply allowlist, validate/trim `url` and `description`
    Sanitizer-->>Handler: sanitized_body or validation error
    Handler->>Handler: Extract reporter_ip from CF-Connecting-IP
    Handler->>Handler: Derive user_id from request context
    Handler->>DB: INSERT using sanitized_body + reporter_ip + user_id + server-managed defaults
    DB-->>Handler: insert id / result
    Handler-->>Client: 201 Created with created bug or 400 Validation Error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

quality: high

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main security fix: preventing unauthenticated mass-assignment of protected fields in the POST /bugs endpoint.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_bugs_security.py (1)

23-52: Parametrize this so each protected field is asserted independently.

Right now the test only proves that a payload containing all privileged fields is rejected. It would still pass if three of those fields became writable and only one remained blocked. A per-field case will make the regression guard precise.

Example refactor
 `@pytest.mark.asyncio`
-async def test_create_bug_rejects_privileged_fields(monkeypatch):
+@pytest.mark.parametrize(
+    ("privileged_field", "value"),
+    [
+        ("verified", True),
+        ("user", 10),
+        ("closed_by", 20),
+        ("reporter_ip_address", "1.2.3.4"),
+    ],
+)
+async def test_create_bug_rejects_privileged_fields(monkeypatch, privileged_field, value):
     async def _fake_get_db_safe(_env):
         return object()

     monkeypatch.setattr(bugs_handler, "get_db_safe", _fake_get_db_safe)

     request_body = json.dumps(
         {
             "url": "https://example.com/vuln",
             "description": "test",
-            "verified": True,
-            "user": 10,
-            "closed_by": 20,
-            "reporter_ip_address": "1.2.3.4",
+            privileged_field: value,
         }
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_bugs_security.py` around lines 23 - 52, Replace the single-case
test_create_bug_rejects_privileged_fields with a parameterized pytest (use
`@pytest.mark.parametrize`) that iterates over each protected field name (e.g.,
"verified", "user", "closed_by", "reporter_ip_address") and for each iteration
builds a request_body containing only that one protected field plus the required
normal fields, then calls bugs_handler.handle_bugs (same monkeypatch of
get_db_safe) and asserts response.status == 400 and that payload["message"]
contains "server-managed"; keep the same MockRequest/MockEnv usage and error
expectations but ensure each protected field is asserted independently.
🤖 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/handlers/bugs.py`:
- Around line 196-204: The create handler currently uses a blacklist
(privileged_fields/provided_privileged_fields) but still copies fields like
views, status, is_hidden, rewarded from body; replace this with a positive
allowlist: define client_writable_fields (e.g. title, description, steps, etc.),
build a sanitized_body by copying only those keys from body, and explicitly set
server-managed fields (views=0, is_hidden=False, rewarded=False, status="open",
verified=False, user=server-side-id, closed_by=None, reporter_ip_address=request
IP) before persisting; remove any code that reads
views/status/is_hidden/rewarded from body and ensure the same allowlist approach
is used in the create path and the referenced block around
privileged_fields/provided_privileged_fields.

---

Nitpick comments:
In `@tests/test_bugs_security.py`:
- Around line 23-52: Replace the single-case
test_create_bug_rejects_privileged_fields with a parameterized pytest (use
`@pytest.mark.parametrize`) that iterates over each protected field name (e.g.,
"verified", "user", "closed_by", "reporter_ip_address") and for each iteration
builds a request_body containing only that one protected field plus the required
normal fields, then calls bugs_handler.handle_bugs (same monkeypatch of
get_db_safe) and asserts response.status == 400 and that payload["message"]
contains "server-managed"; keep the same MockRequest/MockEnv usage and error
expectations but ensure each protected field is asserted independently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e9cf8556-51cd-4dee-ba25-f42133364f63

📥 Commits

Reviewing files that changed from the base of the PR and between 257b4c6 and c70fcad.

📒 Files selected for processing (2)
  • src/handlers/bugs.py
  • tests/test_bugs_security.py

Comment thread src/handlers/bugs.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/handlers/bugs.py`:
- Around line 214-225: The code reads body["url"] into url and calls len(url)
before ensuring it's a string, which will raise on non-string payloads (e.g.,
url: 123); update the handler to first check that url is an instance of str
(e.g., using isinstance(url, str)) and return error_response("URL must be a
string", status=400) if not, then perform the existing length check and
urlparse/scheme validation (references: variable url, error_response, and the
urlparse block).
- Around line 196-213: The handler currently uses client_writable_fields and
builds sanitized_body to silently drop server-managed fields; instead detect if
any disallowed/server-managed fields (e.g., "verified", "user", "closed_by",
"reporter_ip_address" and any other fields the server owns) are present in the
incoming body and immediately return an HTTP 400 error; update the logic around
client_writable_fields/sanitized_body in the request handling function in
src/handlers/bugs.py to compute forbidden = set(body.keys()) -
client_writable_fields, and if forbidden is non-empty respond with a 400 and a
message listing those forbidden fields rather than proceeding to create the
resource.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 05e06a76-947a-4223-bed5-eedbe7381ef0

📥 Commits

Reviewing files that changed from the base of the PR and between c70fcad and 880d9b4.

📒 Files selected for processing (2)
  • src/handlers/bugs.py
  • tests/test_bugs_security.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_bugs_security.py

Comment thread src/handlers/bugs.py
Comment thread src/handlers/bugs.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/handlers/bugs.py`:
- Around line 222-229: The code strips and validates url into the local variable
url but later still binds sanitized_body.get("url"), so persist the normalized
value by using the local url variable in the INSERT/bind instead of
sanitized_body.get("url"); also validate description before the INSERT by
fetching description = sanitized_body.get("description"), confirming it's a
string and that description.strip() is non-empty, returning
error_response("Description is required", status=400) for blank values, and then
use the validated description variable when binding to the DB.
- Around line 284-300: The binds are converting valid numeric zeroes to None via
the "... or None" pattern; update the numeric fields (e.g., the uses of
sanitized_body.get("score") and sanitized_body.get("cve_score") in this
parameter list, and any other numeric binds using "or None") to not use "or
None" — instead pass the raw .get(...) result or use a presence check like
sanitized_body.get("score") if "score" in sanitized_body else None so that
explicit 0 is preserved while still returning None when the key is absent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 17172594-f6ad-4cb5-bb98-f2d56c6b45da

📥 Commits

Reviewing files that changed from the base of the PR and between 880d9b4 and 7788122.

📒 Files selected for processing (2)
  • src/handlers/bugs.py
  • tests/test_bugs_security.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_bugs_security.py

Comment thread src/handlers/bugs.py
Comment thread src/handlers/bugs.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/handlers/bugs.py (1)

273-281: Consider simplifying attribute access pattern.

The hasattr/getattr pattern can be simplified using getattr with a default value. This also addresses the static analysis hint about using getattr with constant attributes.

🛠️ Suggested simplification
-            # Server-side identity (if an auth layer attaches one to request).
-            user_id = None
-            if hasattr(request, "user_id"):
-                user_id = getattr(request, "user_id")
-            elif hasattr(request, "user"):
-                request_user = getattr(request, "user")
-                if isinstance(request_user, dict):
-                    user_id = request_user.get("id")
-                else:
-                    user_id = getattr(request_user, "id", None)
+            # Server-side identity (if an auth layer attaches one to request).
+            user_id = getattr(request, "user_id", None)
+            if user_id is None:
+                request_user = getattr(request, "user", None)
+                if request_user is not None:
+                    if isinstance(request_user, dict):
+                        user_id = request_user.get("id")
+                    else:
+                        user_id = getattr(request_user, "id", None)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/handlers/bugs.py` around lines 273 - 281, The code repeatedly uses
hasattr/getattr to extract a user id from request; simplify by using getattr
with defaults: first try user_id = getattr(request, "user_id", None); if that is
None, get request_user = getattr(request, "user", None) and then set user_id =
request_user.get("id") if isinstance(request_user, dict) else
getattr(request_user, "id", None); update the block that references request,
user_id and request_user to use these getattr defaults and remove the hasattr
checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/handlers/bugs.py`:
- Around line 273-281: The code repeatedly uses hasattr/getattr to extract a
user id from request; simplify by using getattr with defaults: first try user_id
= getattr(request, "user_id", None); if that is None, get request_user =
getattr(request, "user", None) and then set user_id = request_user.get("id") if
isinstance(request_user, dict) else getattr(request_user, "id", None); update
the block that references request, user_id and request_user to use these getattr
defaults and remove the hasattr checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: OWASP-BLT/coderabbit/.coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 69970cd7-b172-40c0-bd1d-c6b80c733a61

📥 Commits

Reviewing files that changed from the base of the PR and between 7788122 and 310523e.

📒 Files selected for processing (2)
  • src/handlers/bugs.py
  • tests/test_bugs_security.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_bugs_security.py

@SatishKumar620
Copy link
Copy Markdown
Contributor

@coderabbitai Please assess the PR's completeness and merge readiness.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== src/handlers/bugs.py ==="
cat src/handlers/bugs.py

echo ""
echo "=== tests/test_bugs_security.py ==="
cat tests/test_bugs_security.py

echo ""
echo "=== List all test files ==="
fd -e py . tests/

echo ""
echo "=== Check for requirements or dependency files ==="
fd -e txt -e toml -e cfg -e ini . --max-depth 2

echo ""
echo "=== Check project structure ==="
tree -L 3 2>/dev/null || find . -maxdepth 3 -type f -name "*.py" | head -50

Length of output: 24965


🏁 Script executed:

#!/bin/bash
echo "=== src/router.py ==="
cat src/router.py

echo ""
echo "=== src/main.py ==="
cat src/main.py

echo ""
echo "=== migrations/0002_add_bugs.sql ==="
cat migrations/0002_add_bugs.sql

echo ""
echo "=== tests/conftest.py ==="
cat tests/conftest.py

echo ""
echo "=== src/handlers/auth.py (first 80 lines) ==="
head -80 src/handlers/auth.py

echo ""
echo "=== pyproject.toml ==="
cat pyproject.toml

Length of output: 18979


@SatishKumar620 Here is a detailed merge readiness assessment for PR #42.


PR #42 — Merge Readiness Assessment

✅ What the PR Does Well

Area Status
Core security fix (mass-assignment) ✅ Positive allowlist correctly blocks verified, user, closed_by, reporter_ip_address, views, is_hidden, rewarded, status
Server-managed defaults views=0, verified=0, status="open", is_hidden=0, rewarded=0, closed_by=NULL are all hardcoded server-side
reporter_ip handling ✅ Read from CF-Connecting-IP header only — never from the JSON body
URL & description validation ✅ Type check, trim, length ≤200, http/https scheme, netloc presence
Test coverage (happy path + main rejections) ✅ 6 tests cover forbidden fields, allowlist defaults, non-string/blank URL and description
SQL injection safety ✅ All queries use parameterized binds

⚠️ Issues That Should Be Addressed Before Merge

🔴 High Severity

  1. No authentication enforcement on POST /bugs
    The router (src/main.py) has no JWT middleware, and bugs.py never checks for an authenticated user before accepting a bug submission. Any anonymous internet user can create bugs. If this is intentional (public reporting), it must be explicitly documented; otherwise, JWT validation from auth.py should be applied.

  2. domain and hunt accept any integer without FK existence check
    Both are in client_writable_fields but no query validates that the submitted domain or hunt ID actually exists. A non-existent ID will hit the DB's foreign key constraint and return a raw 500 Internal Server Error instead of a clean 400 Bad Request.

🟡 Medium Severity

  1. score is client-writable with no range or semantic validation
    A client can submit "score": -99999 or "score": 9999999. The column has a DB-level integer range check (-2147483648 to 2147483647) but no business-logic bounds. Consider restricting to a valid range (e.g., 0–100) in the handler.

  2. cve_id, github_url, and user_agent length is not validated in the handler
    The migration enforces LENGTH(cve_id) <= 16, LENGTH(github_url) <= 200, and LENGTH(user_agent) <= 255 via DB CHECK constraints. A violation produces an unhandled 500, not a descriptive 400. These should be pre-validated:

    # Example additions to the handler
    cve_id = sanitized_body.get("cve_id")
    if cve_id is not None and len(str(cve_id)) > 16:
        return error_response("cve_id must be 16 characters or less", status=400)
    
    github_url = sanitized_body.get("github_url")
    if github_url is not None and len(str(github_url)) > 200:
        return error_response("github_url must be 200 characters or less", status=400)
  3. Missing test coverage for several validation branches
    The following cases have handler code but no tests:

    • URL with invalid protocol (e.g., ftp://example.com) → expects 400
    • URL longer than 200 characters → expects 400
    • URL with no valid domain (e.g., http://) → expects 400
    • domain with a non-integer/non-existent value
    • cve_id exceeding 16 characters

🟢 Minor / Code Quality

  1. Redundant conditional .get() pattern throughout the INSERT block

    # Current (verbose and unnecessary)
    sanitized_body.get("markdown_description") if "markdown_description" in sanitized_body else None
    # Simpler equivalent
    sanitized_body.get("markdown_description")  # already returns None if missing
  2. Commit message mentions "normalize url" but no normalization occurs
    The handler strips whitespace and validates the URL, but does not normalize it (e.g., lowercase scheme/host, strip fragments like #section). If normalization is intended (as suggested by the commit message), it should be added; otherwise the commit message should be updated.


Summary

Overall merge readiness: NOT READY (needs fixes before merge)
Category Finding
Core security fix ✅ Correct and well-tested
Authentication 🔴 POST /bugs is fully unauthenticated — must be decided and documented
FK validation (domain, hunt) 🔴 Missing — causes 500 on invalid IDs
Input length pre-validation 🟡 Missing for cve_id, github_url, user_agent
score bounds 🟡 No semantic limits
Test coverage gaps 🟡 URL protocol, URL length, invalid domain format untested
Code cleanliness 🟢 Minor verbosity in .get() pattern

The core mass-assignment vulnerability fix is sound and correctly implemented. However, the PR should resolve at minimum the authentication decision (#1) and the FK existence validation (#2) before merging to avoid introducing new 500-error paths.

Copy link
Copy Markdown
Contributor

@SatishKumar620 SatishKumar620 left a comment

Choose a reason for hiding this comment

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

Add authentication enforcement on POST /bugs and fix other issues

@Kunal241207
Copy link
Copy Markdown
Contributor Author

@coderabbitai please review the PR strictly within its intended scope (fixing the mass-assignment vulnerability in the bug creation handler).
highlight only issues that are critical, introduce regressions, or would block safe merge of this specific change.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Tip

For best results, initiate chat on the files or code changes.

@Kunal241207, focusing strictly on the mass-assignment fix itself — here is the scoped assessment:


✅ Core Fix — Correct and Safe to Merge

The mass-assignment protection is correctly implemented:

  • The positive allowlist (client_writable_fields) properly covers all server-managed fields named in the PR description: verified, user, closed_by, reporter_ip_address, views, is_hidden, rewarded, status.
  • Any request body key not in the allowlist is rejected with HTTP 400 — the check is straightforward and has no obvious bypass.
  • All server-managed values are hardcoded server-side and never read from the client body after sanitization.
  • The 6 new tests directly cover the critical attack vectors (forbidden field injection, allowlist defaults with server enforcement, invalid URL types/blank URLs).
  • No regressions are introduced for valid existing requests — the happy path is preserved.

🔴 One Critical Issue Within Scope

description validation is new behavior but not tested for the blank/whitespace case via the API path.

The handler now rejects a blank description with a 400. However, looking at the tests, the test_blank_url test covers URL but there is no analogous test_blank_description test. If this validation was not present before this PR, it is a behavior change introduced by this PR that could silently break existing clients sending empty descriptions. Consider adding:

async def test_blank_description():
    body = {"url": "https://example.com", "description": "   "}
    req = MockRequest(body)
    result = await handle_create_bug(req, MockEnv(), None)
    assert result["status"] == 400
    assert "description" in result["body"]["error"].lower()

Out-of-Scope (Pre-Existing, Not Introduced by This PR)

The following issues exist in the codebase independently of this change and should not block this PR's merge:

  • Unauthenticated access to POST /bugs
  • Missing FK existence checks for domain/hunt
  • score, cve_id, github_url length/bounds validation

These are valid follow-up items but are pre-existing concerns.


Verdict: The mass-assignment fix is correct and safe. The one item worth addressing before merge is adding a test for the newly-introduced description blank validation to ensure it's intentional and tested.

@Kunal241207
Copy link
Copy Markdown
Contributor Author

@coderabbitai I think this may be a misread — blank description validation is already covered by test_create_bug_rejects_blank_description, which exercises the API path. Please let me know if there are any remaining blockers strictly within the scope of the mass-assignment fix.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
grep -n "blank_description\|test_create_bug_rejects" tests/test_bugs_security.py

Length of output: 341


@Kunal241207, you are correct — I apologize for the misread. test_create_bug_rejects_blank_description is present at Line 236 of tests/test_bugs_security.py, and test_create_bug_rejects_non_string_description is also there at Line 205. Both description validation branches are fully covered.

Scoped verdict (mass-assignment fix only):

There are no remaining blockers within the scope of this PR. The fix is correct and complete:

  • ✅ Positive allowlist blocks all server-managed fields
  • ✅ Server-managed values are hardcoded and never read from the client body
  • ✅ All critical attack vectors are tested (forbidden fields, allowlist defaults, URL and description validation)
  • ✅ No regressions to the happy path

The PR is safe to merge from a mass-assignment fix perspective.

Copy link
Copy Markdown
Contributor

@yashhzd yashhzd left a comment

Choose a reason for hiding this comment

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

Good work on this security fix. Reviewed the diff, tests, and all CodeRabbit feedback threads — the latest revision addresses every actionable comment.

What's done well:

  • Positive allowlist approach (client_writable_fields set) is the correct pattern for mass-assignment prevention — much stronger than a blocklist
  • Server-managed fields (views=0, verified=0, status="open", is_hidden=0, rewarded=0) are now explicitly hardcoded in the INSERT, not derived from user input
  • CF-Connecting-IP is the correct Cloudflare header for real client IP
  • The if "key" in sanitized_body else None pattern correctly preserves falsy values like score=0 (the earlier or None pattern was a bug — addressed in latest commit)
  • URL and description validation now checks type, strips whitespace, and rejects blanks before hitting the DB
  • 263 lines of focused security tests covering forbidden fields, allowlist enforcement, type validation, and blank input rejection

Minor observations (non-blocking):

  1. The user_agent field is in the client allowlist, but you could also auto-capture it from request.headers.get("User-Agent") similar to how reporter_ip is extracted from CF-Connecting-IP — this would prevent spoofing. Not required for this PR though.
  2. The forward-compatible request.user_id / request.user resolution pattern is a nice touch for future auth middleware integration.
  3. Re: SatishKumar620's request for auth enforcement — I'd consider that a separate concern. This PR's scope (input sanitization + server defaults) is correct and self-contained. Auth middleware is a larger architectural change that should be its own PR.

The INSERT still uses raw SQL rather than the project's ORM (Bug.create()). Given DonnieBLT's preference for ORM usage (noted on other PRs), this might come up during maintainer review — but the security fix itself is solid and shouldn't be blocked on that refactor.

LGTM — approving.

@owasp-blt owasp-blt bot added has-peer-review PR has received peer review and removed needs-peer-review PR needs peer review labels Mar 14, 2026
Copy link
Copy Markdown
Contributor

@SatishKumar620 SatishKumar620 left a comment

Choose a reason for hiding this comment

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

LGTM!

@Kunal241207
Copy link
Copy Markdown
Contributor Author

@DonnieBLT, Could you please check this? Thanks!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants