Repository navigation
feat(detector): add Ruby package inventory and RubyGems/Bundler config audit - #258
Merged
Merged
Conversation
…g audit Adds passive Ruby inventory and RubyGems/Bundler configuration audit to enterprise telemetry as optional ruby_inventory and ruby_config_audit sections, collected in a new ruby_scan phase. Declared, locked, installed and cached evidence stay separate. Sources that are shared, unreadable, cancelled or budget-limited are partial so prior evidence is never retired on unproven absence. The collector never executes Ruby, Bundler, gemspecs or Gemfiles, never extracts archives, and reports credentials as configured only.
There was a problem hiding this comment.
🟡 Changes recommended
Credential-read protections, gemspec selection and discovery completeness have unresolved correctness and privacy issues.
3 open issues
What changed in this PR
Adds passive Ruby package inventory and RubyGems/Bundler configuration auditing to enterprise telemetry, building on the existing guarded collectors. Community output remains unchanged.
Changes:
- Collects declared, locked, installed and cached Ruby package evidence.
- Adds configuration auditing, credential redaction and a
ruby_scantelemetry phase. - Adds parser, scanner, golden-contract and fuzz coverage, plus documentation.
| File | Description |
|---|---|
| SCAN_COVERAGE.md | Documents Ruby evidence, discovery, limits and privacy. |
| README.md | Introduces Ruby configuration auditing. |
| internal/telemetry/telemetry.go | Adds Ruby payload sections and collection phase. |
| internal/telemetry/telemetry_out_test.go | Checks Ruby payload compatibility and omission. |
| internal/safepath/reader_test.go | Tests guarded Windows volume-root listing. |
| internal/safepath/open_windows.go | Allows directory opens at Windows volume roots. |
| internal/model/ruby_golden_test.go | Validates the shared Ruby wire contract. |
| internal/model/model.go | Defines Ruby inventory and audit models. |
| internal/executor/user_aware.go | Delegates process environment-name listing. |
| internal/executor/mock.go | Implements mock environment-name listing. |
| internal/executor/executor.go | Adds the EnvNames executor method. |
| internal/executor/env_prefix_test.go | Tests environment-name listing across executors. |
| internal/detector/rubyscan_test.go | Covers inventory, guards, limits and cancellation. |
| internal/detector/rubymetadata.go | Parses Ruby manifests, locks, stubs and archives. |
| internal/detector/rubymetadata_test.go | Tests metadata parsing and bounded archive handling. |
| internal/detector/ruby_golden_test.go | Checks scanner output against the shared fixture. |
| internal/detector/fuzz_test.go | Adds Ruby metadata fuzz coverage. |
| internal/detector/configaudit/rubyconfig.go | Collects and audits RubyGems/Bundler settings. |
| internal/detector/configaudit/rubyconfig_test.go | Tests redaction, precedence and unsupported input. |
| internal/detector/composerscan_test.go | Extends the shared test executor with EnvNames. |
Review effort: Balanced
| continue | ||
| } | ||
| visited[c.key(path)] = true | ||
| ref := c.referenced(path, "manifest", f.src) |
| ref.path = v.text | ||
| case k == "name" && v.kind == 's': | ||
| ref.name = v.text | ||
| case k == "glob" && v.kind == 's', k == "development_group" && (v.kind == 's' || v.kind == 'y'): |
Comment on lines
+340
to
+348
| entries, _, err := c.fs(p, 0).ReadDirLimit(p, maxRubyListEntries) | ||
| if err != nil { | ||
| if !errors.Is(err, fs.ErrNotExist) { | ||
| reason := c.reason(err, p) | ||
| c.degrade(nil, reason) | ||
| reasons = append(reasons, reason) | ||
| } | ||
| continue | ||
| } |
7 of 9 tasks
ashishkurmi
approved these changes
Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


What does this PR do?
Adds passive Ruby package inventory and RubyGems/Bundler configuration audit to enterprise telemetry through optional
ruby_inventoryandruby_config_auditsections, collected in a newruby_scanphase.Gemfile/gems.rb(includingeval_gemfileandgemspecreferences) and project gemspecs, locked packages fromGemfile.lock/gems.locked(GEM, GIT, PATH and PLUGIN sources,CHECKSUMS), installed and default gems from gemspec stubs, Bundler git checkouts, and cached.gemarchives and vendor caches.~/.gem, the XDG data directory, verifiedGEM_HOME/GEM_PATHand gemrcgemhome/gempath. A verifiedBUNDLE_GEMFILEreplaces the default manifest in its directory.GEMRC,~/.gemrc, system and interpreteretc/gemrc) and Bundler (process, global, project-local and manifest) settings per ambient and project context. Credentials, including dynamicBUNDLE_*host keys and URL userinfo, are reported as configured only;~/.gem/credentialsis checked for existence only. Emits findingsruby-001throughruby-004.EnvNames(prefix), which returns environment variable names only (never values) for dynamic Bundler key classification.FuzzRubyMetadatatarget.The collector never executes Ruby, Bundler, gemspecs, Gemfiles, plugins or discovered commands, and never reads credential contents.
.gemarchives are read bounded and never extracted; YAML tags are never instantiated and aliases never expanded. Community output remains unchanged. Updates README and scan-coverage documentation. Release versioning stays unchanged.Type of change
Testing
send-telemetry --force-scanuploads on all three platforms, independently checked through deployed REST and MCP.make lint, full race-enabledmake test, and isolated-homemake smoke(45/45).go vet, tidy without dependency drift, formatting and diff checks.8fe99ce, including fuzzing and StepSecurity checks.Native and deployed checks cover package identities, evidence and version/platform grouping, configuration precedence/redaction, uninstall and whole-project removal, partial-source stale retention and recovery, pagination, filter/cursor contracts, device counts, the fresh configuration index and actual MCP parity. A 50-project workload per platform was measured and published without losing package locations.
The final fix handles Linux multiarch gem roots, canonical runtime/config aliases and guarded Windows volume-directory discovery. Focused before/after probes and the existing scanner suites pass. Local gosec reports 24 existing findings, with none in the changed source files. The golden remains unchanged at
6fbfc578…9424.Remaining release follow-ups: the dedicated automated Ruby integration-test suite and console/OpenAPI work. Less common runtime-manager layouts are source/test coverage, not a claim that each manager was installed natively. No production deployment was performed.
Related Issues
Builds on the NuGet scanner from #239 (merged) and the Composer configuration helpers from #238 (merged).