Skip to content

FIX: Extract Ruby gems required via subpaths or with parentheses in PackageHallucinationScorer (#3692) - #3693

Merged
Roman Lutz (romanlutz) merged 12 commits into
microsoft:mainfrom
Kaap10:fix/package-hallucination-ruby-subpaths
Nov 4, 2026
Merged

Roman Lutz (romanlutz) merged 12 commits into
microsoft:mainfrom
Kaap10:fix/package-hallucination-ruby-subpaths

Conversation

@Kaap10

Copy link
Copy Markdown
Contributor

Description

PackageHallucinationScorer checks model-generated code for imports or requires of non-existent packages (OWASP LLM02).

In the Ruby ecosystem (PackageEcosystem.RUBY), the extraction regex previously used ([a-zA-Z0-9_-]+) and required whitespace immediately after require/gem:

PackageEcosystem.RUBY: [
    re.compile(r"^\s*require\s+['\"]([a-zA-Z0-9_-]+)['\"]", re.MULTILINE),
    re.compile(r"^\s*gem\s+['\"]([a-zA-Z0-9_-]+)['\"]", re.MULTILINE),
],

This caused two detection gaps:

  1. Subpath requires: Common Ruby requires referencing gem subpaths (e.g. require 'active_support/core_ext', require 'aws-sdk-s3/resource', require 'google/cloud/storage', require 'fake_gem/core') were rejected because / was forbidden in the capture group.
  2. Parenthesized method calls: require("gem_name"), require ("gem_name"), and gem("rails", "~> 7.0") failed because whitespace was strictly demanded directly following the method name.

This PR:

  • Extends the Ruby extraction regexes to accept optional parentheses (?:\s*\(|\s+) and subpath specifiers [^'\"\s]+ for require, and optional parentheses for gem.
  • Reduces Ruby subpath requires to their root gem names in _extract_package_references (reference.split("/", 1)[0]) and ignores local relative and absolute paths (not reference.startswith((".", "/"))), bringing Ruby to architectural parity with the JavaScript ecosystem.
  • Updates module docstrings to document Ruby subpath handling.

Fixes #3692

Tests and Documentation

  • Added 14 test cases in test_ruby_extracts_subpath_and_parenthesized_requires testing subpath requires, parenthesized requires, version constraints, and indentation.
  • Added 11 test cases in test_ruby_ignores_relative_and_absolute_paths verifying that relative paths (./, ../), absolute paths (/), require_relative, and comments are ignored.
  • Added test_ruby_scores_hallucinated_subpath_package_async verifying end-to-end scoring where known subpath packages score False and hallucinated subpath packages score True with accurate rationale and metadata.
  • All 227 tests in test_package_hallucination_scorer.py pass.
  • All 33 tests in test_package_hallucination.py pass.
  • All 1,210 tests in tests/unit/score/regex/ pass.
  • Verified style with ruff check and ruff format.

…ackageHallucinationScorer (microsoft#3692)

Support Ruby gem subpath requires and parenthesized require/gem calls in PackageHallucinationScorer.

Fixes microsoft#3692
@romanlutz Roman Lutz (romanlutz) self-assigned this Nov 1, 2026

@romanlutz Roman Lutz (romanlutz) 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.

The additional Ruby syntax support is useful, but the newly matched inputs have correctness and performance problems. The concrete cases are inline.

@Kaap10

Copy link
Copy Markdown
Contributor Author

Thanks for thorough review Roman Lutz (@romanlutz), addressed in the latest commit.

@romanlutz Roman Lutz (romanlutz) 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.

The update addresses the explicit feature extensions, interpolation examples, Windows paths, and the original overlapping-whitespace regex. I found two additional problems in the revised implementation. This pass is source-only: the new unbounded candidate expansion keeps the execution safety hold in place, so I did not run the updated code or tests.

…d avoid near-name false negatives (microsoft#3693)

- Enforce limits on feature length (255) and path depth (5) to bound prefix candidate generation, falling back cleanly to the root segment on inputs exceeding limits.
- Support multiline parenthesized require and gem calls with internal line breaks while keeping unparenthesized calls ReDoS-safe with strict horizontal spacing.
- Remove arbitrary underscore stripping to prevent near-name false negatives (e.g. j_s_o_n, review_ghost).
- Map standard Rails namespaces (e.g. active_support -> activesupport) explicitly.
- Add tests for multiline calls via public scoring, deep path performance control, and negative near-name cases.
Copilot AI balanced review requested due to automatic review settings November 2, 2026 13:00

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@Kaap10

Copy link
Copy Markdown
Contributor Author

Roman Lutz (@romanlutz), thanks for the feedback! In the latest commit, we bounded candidate expansion to 255 chars / 5 segments (with a 4,096-component performance control test) and enabled multiline parenthesized require/gem calls tested via the public score_message_async entry point. We also eliminated arbitrary underscore stripping to reject near-names like j_s_o_n and review_ghost, explicitly mapping standard Rails aliases instead. All 245 tests and type checks pass cleanly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…crosoft#3693)

- Decouple feature path depth from candidate prefix depth by inspecting leading prefixes up to 5 segments longest-first.
- Resolve 6+ segment paths like 'google/cloud/spanner/v1/spanner/client' to their registered gem 'google-cloud-spanner-v1'.
- Retain root namespace alias evaluation (e.g., active_support -> activesupport) even when feature path length exceeds 255 chars.
- Add test_ruby_scores_deep_subpath_owning_gem_correctly_async verifying public scoring of deep subpaths.

@romanlutz Roman Lutz (romanlutz) 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.

i'll make a couple fixes but this looks great!

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use public scoring for the added Ruby regression cases after the expectation-aware hook migration. Update the NCC research index link to its canonical destination for strict documentation checks.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Nov 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 4, 2026
@romanlutz
Roman Lutz (romanlutz) added this pull request to the merge queue Nov 4, 2026
Merged via the queue into microsoft:main with commit f2dd135 Nov 4, 2026
65 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.

BUG: PackageHallucinationScorer fails to extract Ruby gems required via subpaths or with parentheses

3 participants