Governance
Configure fix approvals, protected paths, and account settings in one place.
Research
How to reason about C extensions, garbage collection, and input reachability without overstating the impact of a library defect.
A Ruby application may rely on native extensions for database access, serialization, compression, or parsing. The application's high-level interface does not eliminate the memory-safety responsibilities inside those extensions. An audit should identify where Ruby values become pointers, lengths, callbacks, or retained native objects.
Start with the application's locked dependencies and its actual build artifacts. Determine whether an extension uses a bundled library or a system-provided one. Two installations with the same Ruby gem version can have different native dependency exposure when their build options, operating-system packages, or loaded shared libraries differ.
An extension needs a clear rule for how long a native object remains valid and which runtime owns its cleanup. Registering a callback, redefining a function, or storing a pointer for later use can create a lifetime different from that of the immediate Ruby method call. Garbage collection makes that distinction especially important.
The sqlite3-ruby advisory GHSA-28hh-pr2h-2w89 provides a useful example: redefining a custom SQLite function name with different arities or encodings can cause invalid memory access after garbage collection. The maintainers classify that issue as low severity and identify 2.9.5 as the fixed gem version. Do not silently inflate that finding into remote code execution.
A reproducible native crash demonstrates a memory-safety problem under specific conditions. To assess an application, establish whether an untrusted actor can influence those conditions: input bytes, serialization options, callback registration, or the relevant sequence of operations. Trusted application setup is a different surface from a public request body.
Trace the complete path with the actual configuration and privilege model. Record authentication requirements and any transformations before the extension receives data. If a required condition is not reachable, retain that limitation rather than treating every installed dependency as proof of an exploitable production path.
Build the relevant extension with memory instrumentation where supported, then exercise its actual integration rather than a rewritten approximation. Keep the Ruby version, compiler, extension revision, and runtime options in the result. A failing native test should produce a stable artifact that a maintainer can reproduce without production data.
Include lifecycle transitions: repeated calls, object release, garbage collection, callback replacement, and error unwinding. A test that only calls an operation once may miss the lifetime boundary responsible for the defect. Preserve successful ordinary behavior alongside the negative case so a repair does not simply disable the feature.
Prefer a maintained upstream fix over a local workaround when available. Verify that the deployed process loads the repaired extension and linked libraries, not a cached native build from an earlier image layer. Rebuild dependent artifacts when the package manager does not automatically invalidate them.
A dependency report should state both the library finding and the application-level conclusion. Explain what was confirmed, what could not be reproduced, and which conditions remain uncertain. This keeps the work useful to defenders while avoiding unsupported discovery counts, historical ownership claims, or assumptions that a native defect has identical impact in every Ruby service.