Skip to content

fix: address 7 Sonar security hotspots flagged on PR #29 - #36

Merged
cemililik merged 1 commit into
developmentfrom
fix/sonar-hotspots-pr29
May 6, 2026
Merged

cemililik merged 1 commit into
developmentfrom
fix/sonar-hotspots-pr29

Conversation

@cemililik

@cemililik cemililik commented May 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Address all 7 Security Hotspots flagged by SonarCloud on PR #29
(development → main v0.5.5 consolidation), where the Quality
Gate is currently failing on C Security Rating + 7 hotspots.

After investigation each is a verified false positive — the
code is either the security guard itself, a test fixture
verifying the guard rejects insecure schemes, or a string argument
that is never written to disk. This PR corrects the in-code
documentation so a reviewer marking each hotspot as "Safe" in the
SonarCloud UI has the rationale on hand.

Hotspot-by-hotspot disposition

# File:Line Rule Sev Action
1 tools/check_site_claims.py:240 S5852 (regex DoS) MEDIUM NOSONAR moved onto regex literal line + rule tag
2-3 forgelm/_http.py:202 S5332 (http) LOW × 2 NOSONAR onto rejection f-string + rule tag + paragraph comment
4 tests/test_webhook.py:235 S5332 (http docstring) LOW Method-level NOSONAR + docstring rephrase
5 tests/test_webhook.py:238 S5332 (regex match) LOW NOSONAR onto match= line + rule tag
6 tests/test_webhook.py:256 S5332 (ftp docstring) LOW Method-level NOSONAR + docstring rephrase
7 tests/test_webhook.py:419 S5443 (publicly-writable dir) LOW /tmp/staged fixture replaced with ./outputs/run-r/final_model.staging

Why each is a false positive

Self-review gauntlet (all green at HEAD 755b909)

Check Result
ruff format --check . + ruff check . ✅ clean
check_bilingual_parity --strict ✅ 39/39
check_anchor_resolution --strict ✅ 264/264
check_cli_help_consistency --strict ✅ 425/425
check_site_claims --strict ✅
check_field_descriptions --strict ✅
pytest --no-cov -q tests/ ✅ 1436 passed / 14 skipped
forgelm --config config_template.yaml --dry-run ✅ exit 0
tests/test_webhook.py (targeted) ✅ 32/32
tests/test_check_site_claims.py (targeted) ✅ 9/9

Note for the SonarCloud review pass

Security Hotspots normally use SonarCloud's "Mark as Safe"
review workflow (not # NOSONAR, which is for issues). Even
after these in-code clarifications the maintainer may still need
to mark each of the 7 entries as Safe in the SonarCloud UI per
the standard hotspot-review process. The expanded in-code
comments make that UI review a 2-minute audit rather than a
re-derivation from scratch.

Once this PR merges to development, PR #29's Sonar quality gate
should re-scan and either drop the C rating to A (if NOSONAR is
honoured for hotspots in the configured Sonar setup) OR leave the
hotspots in TO_REVIEW state for the maintainer to mark Safe.

🤖 Generated with Claude Code

Summary by Sourcery

Clarify and document SonarCloud security hotspots that are verified false positives so they can be reviewed and marked safe without altering runtime behavior.

Enhancements:

  • Document the HTTP scheme rejection guard and regex usage with targeted NOSONAR annotations and clearer comments to explain why Sonar security rules do not apply.
  • Rephrase webhook-related test docstrings and inline comments to describe security behavior without implying insecure outbound calls.
  • Adjust a webhook test fixture path from a /tmp location to a project-relative model path shape to better reflect real payloads while avoiding public-temp-directory concerns.

Tests:

  • Refine webhook tests to explicitly verify rejection of insecure or unsupported URL schemes while remaining compliant with SonarCloud hotspot analysis.

Address all 7 Security Hotspots flagged by SonarCloud on PR #29
(`development → main` v0.5.5 consolidation). Each is a verified
false positive — the code is the security guard itself, a test
fixture verifying the guard rejects insecure schemes, or a string
argument that is never written to disk. The fix is a combination
of moving existing NOSONAR markers onto the literal violation
line (Sonar's per-line NOSONAR parser is strict) and replacing
one `/tmp/`-shaped string fixture with a project-relative path.

## Hotspot-by-hotspot disposition

### #1 — `tools/check_site_claims.py:240` — S5852 ReDoS (MEDIUM)
The regex `r"^\s+([a-z][a-z0-9-]+-[a-z0-9-]+)\s{2,}"` runs against
*controlled static HTML* the build emits — never user input. The
backtracking risk is unreachable. The existing NOSONAR was on the
trailing line (after the closing `)`); Sonar didn't apply it to
line 240. Move the NOSONAR onto the regex literal line and tag
the rule explicitly (`# NOSONAR python:S5852`).

### #2-3 — `forgelm/_http.py:202` — S5332 HTTP insecure (LOW × 2)
This is the rejection f-string of the `safe_post` security guard
itself: `f"http:// blocked (use https://); url={_mask_netloc(url)}"`.
Naming the blocked scheme is intentional so operators see WHY the
call failed. Move the NOSONAR onto the f-string line + tag
`python:S5332` + add a paragraph-level comment explaining the
guard semantics.

### #4 — `tests/test_webhook.py:235` — S5332 (LOW, docstring)
Sonar flagged the literal `http://` in a docstring describing the
test that verifies http:// IS BLOCKED. Tag the test method with
`# NOSONAR python:S5332` and rephrase the docstring to lead with
"Verify plain-HTTP URLs are rejected…" so the security intent is
inverted-explicit.

### #5 — `tests/test_webhook.py:238` — S5332 (LOW, regex match)
`pytest.raises(HttpSafetyError, match="http://")` — the literal
is a regex pattern matching the rejection error message, not an
outbound call. Tag with `# NOSONAR python:S5332`.

### #6 — `tests/test_webhook.py:256` — S5332 (LOW, ftp docstring)
Same shape as #4 but for the `test_unsupported_scheme` docstring
that names ftp / file as the rejected schemes. Tag the method +
rephrase docstring.

### #7 — `tests/test_webhook.py:419` — S5443 publicly-writable dir
`model_path="/tmp/staged"` — a string fixture passed to
`notify_awaiting_approval`. Nothing is written to disk; the test
asserts the webhook payload schema. Sonar's S5443 fires on the
`/tmp/` literal regardless. Replace with the project-relative
shape `./outputs/run-r/final_model.staging` (the actual production
shape), which is honest about what `model_path` looks like in
real deployments and side-steps the false positive without
needing a NOSONAR. Inline comment documents why no filesystem
write happens.

## Verification

All 5 strict CI guards green at HEAD:
| Check | Result |
|---|---|
| `ruff format --check .` + `ruff check .` | ✅ clean |
| `check_bilingual_parity --strict` | ✅ 39/39 |
| `check_anchor_resolution --strict` | ✅ 264/264 |
| `check_cli_help_consistency --strict` | ✅ 425/425 |
| `check_site_claims --strict` | ✅ artefacts/templates/GPU/version |
| `check_field_descriptions --strict` | ✅ |
| `pytest --no-cov -q tests/` | ✅ 1436 passed / 14 skipped |
| `forgelm --config config_template.yaml --dry-run` | ✅ exit 0 |

`tests/test_webhook.py` (32 cases) and
`tests/test_check_site_claims.py` (9 cases) both pass under the
new NOSONAR + tmp-path-replacement state.

## Note for the SonarCloud review pass

Security Hotspots normally use SonarCloud's "Mark as Safe"
review workflow rather than `# NOSONAR` (which is for issues).
Even after these in-code clarifications, the maintainer may
still need to mark each of the 7 entries as Safe in the SonarCloud
UI. The in-code comments above document the rationale so the UI
review is a 2-minute audit rather than a re-derivation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

@sourcery-ai

sourcery-ai Bot commented May 6, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Clarifies and documents seven SonarCloud security hotspots (all false positives) by adding targeted NOSONAR annotations and explanatory comments around regex usage, HTTP/FTP scheme guards, and a webhook test fixture path, without changing production behavior except for making a test fixture path more realistic and clearly non-/tmp-based.

File-Level Changes

Change Details Files
Document and suppress the regex DoS (S5852) hotspot on static-HTML processing in the site claims checker.
  • Moved the NOSONAR annotation onto the regex literal line used in re.finditer.
  • Added the explicit Sonar rule tag (python:S5852) and rationale that the regex runs only on controlled static HTML, so ReDoS is not exploitable.
  • Reformatted the re.finditer call over multiple lines for clarity.
tools/check_site_claims.py
Clarify that safe_post’s HTTP rejection path is the security guard itself and intentionally mentions http:// in its error message.
  • Expanded the pre-raise comment to explain that the error string intentionally names the blocked scheme so operators understand failures, and that this is the inverse of an insecure call site.
  • Moved and refined the NOSONAR annotation to the f-string that includes the http:// literal, with an explicit Sonar rule tag (python:S5332) and rationale.
  • Slightly refactored the raise block formatting for readability without changing behavior.
forgelm/_http.py
Refine webhook tests to clearly mark Sonar hotspots as false positives and adjust the model_path fixture to avoid /tmp while reflecting a realistic project-relative path.
  • Added method-level NOSONAR annotations with explicit rule tags and rationale to the http and unsupported-scheme tests whose docstrings mention http/ftp schemes.
  • Rephrased test docstrings to describe verification of the guard behavior rather than simply restating insecure URLs.
  • Annotated the pytest.raises(match="http://") expression and scheme-blocker fixture lines with NOSONAR + python:S5332 and explanatory comments that these are match patterns and guard-verification fixtures, not outbound calls.
  • Updated the webhook notifier test’s model_path from '/tmp/staged' to './outputs/run-r/final_model.staging' and added a comment explaining that this string is only sent in a payload, not written to disk, addressing Sonar S5443 while better matching production-like paths.
tests/test_webhook.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe4d305b-9fd7-4cc8-a28e-3b0898de46a3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sonar-hotspots-pr29

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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Some of the inline NOSONAR comments (e.g., around safe_post and the webhook tests) are quite verbose and repeat the same rationale; consider consolidating the explanation into a single, shorter comment per function/test and keeping the per-line NOSONAR notes minimal to preserve readability.
  • Where you add NOSONAR on entire function definitions (e.g., test_http_block / test_unsupported_scheme), you might be able to narrow the suppression to just the literal-bearing lines to reduce the chance of unintentionally hiding future issues in those functions.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Some of the inline NOSONAR comments (e.g., around `safe_post` and the webhook tests) are quite verbose and repeat the same rationale; consider consolidating the explanation into a single, shorter comment per function/test and keeping the per-line NOSONAR notes minimal to preserve readability.
- Where you add NOSONAR on entire function definitions (e.g., `test_http_block` / `test_unsupported_scheme`), you might be able to narrow the suppression to just the literal-bearing lines to reduce the chance of unintentionally hiding future issues in those functions.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@sonarqubecloud

sonarqubecloud Bot commented May 6, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots

See analysis details on SonarQube Cloud

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refines SonarCloud suppression comments across several files to provide more specific rule identifiers and better documentation for security-related logic. It also updates a test case to use a project-relative path instead of a temporary directory. Feedback includes suggestions to optimize the placement of suppression comments, refine test file paths to ensure robustness, and improve a regular expression to further mitigate potential ReDoS vulnerabilities.

I am having trouble creating individual review comments. Click here to see my feedback.

forgelm/_http.py (206)

medium

The # NOSONAR comment should ideally be placed on the line that triggers the warning. While moving it to the f-string line clarifies the intent, ensure that SonarCloud correctly interprets it when it's part of a function call argument. Also, consider if using a constant for the error message would be cleaner.

                f"http:// blocked (use https://); url={_mask_netloc(url)}"  # NOSONAR python:S5332

tests/test_webhook.py (431)

medium

Using a project-relative path like ./outputs/... is better than /tmp/..., but ensure that the directory ./outputs/run-r/ actually exists or is created during the test if any code ever attempts to resolve it, even if it's just a string in this specific test. This prevents potential FileNotFoundError in future test refactors.

            model_path="outputs/run-r/final_model.staging",

tools/check_site_claims.py (240)

security-medium medium

The regex r"^\s+([a-z][a-z0-9-]+-[a-z0-9-]+)\s{2,}" contains nested repetitions which can lead to polynomial backtracking (ReDoS) if the input is large and crafted. Although the input is 'controlled static HTML', it's safer to optimize the regex. For example, ensuring the hyphenated parts are more strictly defined or using atomic grouping if supported.

        r"^\s+([a-z][a-z0-9]*-[a-z0-9-]+)\s{2,}",  # NOSONAR python:S5852

@cemililik
cemililik merged commit e93f84d into development May 6, 2026
3 of 4 checks passed
@cemililik
cemililik deleted the fix/sonar-hotspots-pr29 branch May 10, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant