Skip to content

Fix variable shadowing crash in get_by_tags and code quality improvements#837

Merged
northdpole merged 3 commits into
OWASP:mainfrom
BEAST04289:fix/code-quality-improvements
Jul 20, 2026
Merged

Fix variable shadowing crash in get_by_tags and code quality improvements#837
northdpole merged 3 commits into
OWASP:mainfrom
BEAST04289:fix/code-quality-improvements

Conversation

@BEAST04289

Copy link
Copy Markdown
Contributor

Summary

Fixes a crash bug and several code quality issues found during codebase review for GSoC 2026 Module B proposal.

Closes #836

Changes

Bug Fix: Variable shadowing crash in get_by_tags() (application/database/db.py)

The loop variable node was overwritten by the return value of self.get_nodes(). When get_nodes() returned [] (empty list), the error handler tried to access node.name on the empty list, causing AttributeError. Renamed to db_node and resolved to preserve the original DB node reference for error logging.

Code Quality Fixes

  • Unreachable code in add_node() (db.py:1585): Removed dead return None after raise ValueError
    • Duplicate import (web_main.py:15,20): Removed redundant from application.utils import oscal_utils, redis
    • Variable typo (gap_analysis.py:44-46): Renamed pentalty to penalty
    • Variable typo (cre_main.py:280): Renamed pending_stadards to pending_standards (4 occurrences)

Testing

  • All changes are localized and do not alter behavior
    • The variable shadowing fix preserves the original node reference for correct error logging

Copilot AI review requested due to automatic review settings March 29, 2026 13:43

Copilot AI 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.

Pull request overview

Fixes a crash in Node_collection.get_by_tags() caused by variable shadowing, and applies a few small code-quality cleanups discovered during review (typos, dead code, redundant import).

Changes:

  • Prevent variable shadowing in get_by_tags() so error logging can’t crash when get_nodes() returns an empty list.
  • Remove unreachable code in add_node() and clean up redundant imports / misspelled local variable names.
  • Rename typos in variables used by gap analysis and standard registration flow.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
application/database/db.py Fixes get_by_tags() variable shadowing crash; removes unreachable return after raise.
application/web/web_main.py Removes redundant oscal_utils, redis import line (keeps the remaining import).
application/utils/gap_analysis.py Fixes misspelled local variable pentaltypenalty.
application/cmd/cre_main.py Fixes pending_stadards typo and updates references to the corrected variable name.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread application/cmd/cre_main.py Outdated
Comment on lines 295 to 297
logger.error(
f"Could not find gap analysis job for for {importing_name} and {standard_name} putting {standard_name} back in the queue"
)

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

Typo in log message: duplicated word for ("job for for …").

Copilot uses AI. Check for mistakes.
Comment thread application/cmd/cre_main.py Outdated
Comment on lines 310 to 312
logger.error(
f"Could not find gap analysis job for for {importing_name} and {standard_name} putting {standard_name} back in the queue"
)

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

Typo in log message: duplicated word for ("job for for …").

Copilot uses AI. Check for mistakes.
Comment thread application/cmd/cre_main.py Outdated
Comment on lines 280 to 299
@@ -295,7 +295,7 @@ def register_standard(
logger.error(
f"Could not find gap analysis job for for {importing_name} and {standard_name} putting {standard_name} back in the queue"
)
pending_stadards.append(standard_name)
pending_standards.append(standard_name)

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

This code appends back into pending_standards while iterating over it. Mutating the list during iteration can lead to unexpected behavior (including repeatedly re-processing the same standard if the exception keeps happening). Consider using a separate retry list/queue (e.g., to_retry.append(...) then extend after the loop), or iterating over a snapshot copy.

Copilot uses AI. Check for mistakes.
Comment thread application/cmd/cre_main.py Outdated
Comment on lines 300 to 314
@@ -310,7 +310,7 @@ def register_standard(
logger.error(
f"Could not find gap analysis job for for {importing_name} and {standard_name} putting {standard_name} back in the queue"
)
pending_stadards.append(standard_name)
pending_standards.append(standard_name)
redis.wait_for_jobs(jobs)

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

This code appends back into pending_standards while iterating over it. Mutating the list during iteration can lead to unexpected behavior (including repeatedly re-processing the same standard if the exception keeps happening). Consider using a separate retry list/queue (e.g., to_retry.append(...) then extend after the loop), or iterating over a snapshot copy.

Copilot uses AI. Check for mistakes.
Comment thread application/database/db.py Outdated
else:
logger.fatal(
"db.get_node returned None for"
"db.get_node returned None for "

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

The fatal log message in this branch is misleading: get_nodes() never returns None (it returns an empty list when nothing matches), and the message references db.get_node rather than get_nodes. This makes the log inaccurate and can confuse debugging; consider updating the wording to reflect an empty result from get_nodes() (and the correct function name).

Suggested change
"db.get_node returned None for "
"get_nodes() returned no documents for "

Copilot uses AI. Check for mistakes.
Comment on lines 983 to 1001
nodes = Node.query.filter(*nodes_where_clause).all() or []
for node in nodes:
node = self.get_nodes(
name=node.name,
section=node.section,
subsection=node.subsection,
version=node.version,
link=node.link,
ntype=node.ntype,
sectionID=node.section_id,
for db_node in nodes:
resolved = self.get_nodes(
name=db_node.name,
section=db_node.section,
subsection=db_node.subsection,
version=db_node.version,
link=db_node.link,
ntype=db_node.ntype,
sectionID=db_node.section_id,
)
if node:
documents.extend(node)
if resolved:
documents.extend(resolved)
else:
logger.fatal(
"db.get_node returned None for"
"db.get_node returned None for "
"Node %s:%s:%s that exists, BUG!"
% (node.name, node.section, node.section_id)
% (db_node.name, db_node.section, db_node.section_id)
)

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

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

There is test coverage for get_by_tags() (see application/tests/db_test.py::test_get_by_tags), but it doesn’t exercise the failure mode fixed here (where get_nodes() returns an empty list for a DB row that matched the initial tag query). Please add a regression test that simulates get_nodes() returning [] for a matching Node row to ensure get_by_tags() doesn’t crash when logging the error path.

Copilot uses AI. Check for mistakes.
@BEAST04289
BEAST04289 force-pushed the fix/code-quality-improvements branch from 276bb39 to 7ae6362 Compare March 29, 2026 14:41
Rename the loop variable so an empty get_nodes() result does not
overwrite the DB node used in the fatal error path. Add regression test.

Co-authored-by: Cursor <cursoragent@cursor.com>
@northdpole

Copy link
Copy Markdown
Collaborator

Rebased the get_by_tags variable-shadowing fix onto current main (minimal: db.py + regression test test_get_by_tags_empty_nodes_regression). Unrelated typo renames from the earlier branch tip were left out. Ready for re-review.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: d1109220-e0a4-4966-ae80-649e6dafda2e

📥 Commits

Reviewing files that changed from the base of the PR and between dad89ed and b9aa494.

📒 Files selected for processing (2)
  • application/database/db.py
  • application/tests/db_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • application/tests/db_test.py
  • application/database/db.py

Summary by CodeRabbit

  • Bug Fixes

    • Fixed tag-based lookups to correctly handle cases where node retrieval returns no items.
    • Returned documents are now included only when matches are actually found, while the diagnostic fatal logging remains consistent and accurate.
  • Tests

    • Added a regression test for tag searches where node retrieval yields an empty list, asserting an empty result and confirming the expected fatal log entry is emitted.

Walkthrough

get_by_tags avoids variable shadowing when resolving matching nodes, handles empty resolutions safely, updates fatal logging, and adds regression coverage for the empty-result path.

Changes

get_by_tags resolution handling

Layer / File(s) Summary
Resolution handling and regression coverage
application/database/db.py, application/tests/db_test.py
get_by_tags resolves each database node into documents only when a result exists, updates the empty-resolution fatal log, and tests the empty-list behavior and logging call.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: robvanderveer, northdpole, paoga87, pa04rth

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR fixes the get_by_tags crash, but it does not include the other linked issue fixes in db.py, web_main.py, gap_analysis.py, or cre_main.py. Add the remaining code-quality fixes from #836, or narrow the linked issue scope if this PR is intended to cover only the crash fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the get_by_tags crash fix and the cleanup work.
Description check ✅ Passed The description matches the implemented crash fix and related cleanup items.
Out of Scope Changes check ✅ Passed The changes stay within scope: a db.py bug fix plus a regression test.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@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: 1

🤖 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 `@application/tests/db_test.py`:
- Around line 143-146: Strengthen the test around collection.get_by_tags by
asserting that get_nodes receives the database node’s original identity fields,
and that logger.fatal is called with those same unchanged values. Keep the
existing empty-result assertion while replacing the call-count-only check with
argument assertions covering the regression contract.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: ca754a79-c39e-4e15-9311-0f9572ea3340

📥 Commits

Reviewing files that changed from the base of the PR and between 2d1aa47 and b1939ff.

📒 Files selected for processing (2)
  • application/database/db.py
  • application/tests/db_test.py

Comment thread application/tests/db_test.py Outdated
BEAST04289 and others added 2 commits July 19, 2026 05:28
Align the fatal message with get_nodes() returning [], update the
regression assertion, and satisfy black so CI lint can pass.

Co-authored-by: Cursor <cursoragent@cursor.com>

@northdpole northdpole left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM after follow-up:

  • shadowing fix is correct and covered by regression test
  • fatal log now accurately describes get_nodes() returning no documents
  • black formatting fixed

Low severity (defensive path), but safe and tested. Merging.

@northdpole
northdpole merged commit 6b49336 into OWASP:main Jul 20, 2026
6 checks passed
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.

Fix variable shadowing crash in get_by_tags and code quality improvements

3 participants