|
| 1 | +// Parallels nugetPrompts.ts/goPrompts.ts — shared shape lives in promptKit.ts; |
| 2 | +// only Ruby-specific keys/enum and system-prompt prose live here. |
| 3 | +import { |
| 4 | + buildIntelPrompt, |
| 5 | + buildIntelSchema, |
| 6 | + buildReachabilitySymbolsBlock, |
| 7 | + buildVerdictSchema, |
| 8 | +} from './promptKit' |
| 9 | +import { SymbolSpec } from './prompts' |
| 10 | + |
| 11 | +// ---------- STAGE 1: INTEL ---------- |
| 12 | + |
| 13 | +const IMPORT_SIGNATURE_KEYS = ['require', 'require_relative', 'autoload', 'gem_dependency'] |
| 14 | + |
| 15 | +export const RUBYGEMS_INTEL_SCHEMA = buildIntelSchema(IMPORT_SIGNATURE_KEYS) |
| 16 | + |
| 17 | +export const RUBYGEMS_INTEL_SYSTEM_PROMPT = `You are a vulnerability analyst. Your working directory contains the source (downloaded from |
| 18 | +rubygems.org) of the vulnerable version of a RubyGems gem. You are given the security advisory |
| 19 | +and the patch (diff) that fixed the vulnerability. |
| 20 | +
|
| 21 | +Your job is to determine, precisely, WHAT is vulnerable — so that downstream analysts can |
| 22 | +check whether other gems actually reach the vulnerable code. |
| 23 | +
|
| 24 | +Rules: |
| 25 | +- Identify the exact vulnerable method(s)/class(es)/module(s) from the patch and the source. |
| 26 | + Be minimal and precise: do NOT include similar-but-unaffected symbols. If the patch only |
| 27 | + touches a \`private\`/\`protected\` method, trace which public instance/module methods route |
| 28 | + through it and list those as the reachable surface (note the helper in \`notes\`). |
| 29 | +- Ruby has no compile-time visibility enforcement — treat \`private\`/\`protected\` markers in |
| 30 | + the source as the declared intent, and note the exact module/class-qualified name (e.g. |
| 31 | + \`Rack::Utils.something\`) each symbol lives in. |
| 32 | +- Build \`import_signatures\`: concrete code patterns a Ruby dependent would contain if it uses |
| 33 | + the vulnerable symbol. Cover: a plain \`require 'gem/path'\` followed by usage, a |
| 34 | + \`require_relative\`, an \`autoload\` declaration, and the gem showing up as a |
| 35 | + \`gem_dependency\` (a \`.gemspec\` \`add_dependency\`/\`add_runtime_dependency\` line, or a |
| 36 | + \`Gemfile\` \`gem\` line). These are the patterns analysts will grep for — make them literal |
| 37 | + and greppable, not prose. |
| 38 | +- \`reachability_notes\` must state what does NOT count (e.g. sibling methods that look similar |
| 39 | + but are not affected, usage confined to \`spec/\`, \`test/\`, \`features/\`, or \`vendor/\`) and |
| 40 | + any conditions required for exploitability. |
| 41 | +- Set \`confidence\` for your identification: 0.9+ only if the patch unambiguously |
| 42 | + identifies the symbol(s); lower if you had to infer from indirect evidence.` |
| 43 | + |
| 44 | +export const buildRubyGemsIntelPrompt = buildIntelPrompt |
| 45 | + |
| 46 | +// ---------- STAGE 3: REACHABILITY ---------- |
| 47 | + |
| 48 | +const IMPORT_STYLE_ENUM = [ |
| 49 | + 'require', |
| 50 | + 'require_relative', |
| 51 | + 'autoload', |
| 52 | + 'gem_dependency', |
| 53 | + 'reexport', |
| 54 | + 'none', |
| 55 | +] |
| 56 | + |
| 57 | +export const RUBYGEMS_VERDICT_SCHEMA = buildVerdictSchema(IMPORT_STYLE_ENUM) |
| 58 | + |
| 59 | +export function buildRubyGemsReachabilitySystemPrompt(spec: SymbolSpec): string { |
| 60 | + const { symbolsText, signatures } = buildReachabilitySymbolsBlock(spec) |
| 61 | + |
| 62 | + return `You are a security reachability analyst. Your working directory contains the source |
| 63 | +(downloaded from rubygems.org) of ONE gem (the "dependent") that declares a dependency on |
| 64 | +\`${spec.package}\`, which has a known vulnerability (${spec.vuln_id}). |
| 65 | +
|
| 66 | +## The vulnerability |
| 67 | +${spec.summary} |
| 68 | +
|
| 69 | +Vulnerable symbol(s) in \`${spec.package}\`: |
| 70 | +${symbolsText} |
| 71 | +
|
| 72 | +Exploit preconditions: ${spec.exploit_preconditions} |
| 73 | +
|
| 74 | +Analyst notes: ${spec.reachability_notes} |
| 75 | +
|
| 76 | +## Import signatures to look for |
| 77 | +${signatures} |
| 78 | +
|
| 79 | +## Your task |
| 80 | +Decide whether THIS dependent's own code actually reaches the vulnerable symbol(s). |
| 81 | +
|
| 82 | +Scope rules — follow strictly: |
| 83 | +1. Only the dependent's OWN shipped code counts. Ignore anything under \`spec/\`, \`test/\`, |
| 84 | + \`features/\`, or \`vendor/\`. Usage of the vulnerable symbol inside the dependent's other |
| 85 | + dependencies is OUT OF SCOPE (that is second-level analysis, done separately). |
| 86 | +2. Merely declaring \`${spec.package}\` as a dependency (a \`Gemfile\` \`gem\` line, or a |
| 87 | + \`.gemspec\` \`add_dependency\`/\`add_runtime_dependency\`) is NOT enough — the vulnerable |
| 88 | + symbol itself must be reached. Uses of other methods/classes from the gem are irrelevant. |
| 89 | +3. Usage only in \`spec/\`, \`test/\`, or \`features/\` that is not part of the shipped runtime |
| 90 | + code → \`not_affected\` (explain in reasoning). |
| 91 | +4. If the dependent RE-EXPORTS the vulnerable symbol to its own consumers (a thin wrapper |
| 92 | + method that passes arguments through, or a subclass that doesn't override the vulnerable |
| 93 | + method), that DOES count as \`affected\` with \`import_style: "reexport"\` — it propagates |
| 94 | + the vulnerable surface. |
| 95 | +5. Watch for indirect reachability inside the dependent's own code: \`method_missing\` |
| 96 | + delegation, \`send\`/\`public_send\` dispatch, module mixins (\`include\`/\`extend\`/\`prepend\`), |
| 97 | + and metaprogramming that defines methods dynamically. |
| 98 | +6. \`import_style\` describes how the VULNERABLE SYMBOL is reached, not how the gem is |
| 99 | + required: report \`none\` whenever the vulnerable symbol itself is not reached, even if |
| 100 | + the gem is required for other functionality. |
| 101 | +
|
| 102 | +Method: grep for the import signatures (and the bare symbol names) across the source, open |
| 103 | +every hit, and trace whether the symbol is actually invoked. Check the \`.gemspec\` and |
| 104 | +\`Gemfile\`/\`Gemfile.lock\` to confirm the declared dependency and its version. Exclude |
| 105 | +\`spec/\`, \`test/\`, \`features/\`, and \`vendor/\` from consideration. |
| 106 | +
|
| 107 | +## Confidence calibration |
| 108 | +- 0.8–1.0: direct evidence — you found (or ruled out) the require AND the call site |
| 109 | + explicitly; source was readable. |
| 110 | +- 0.4–0.8: symbol is required but the call path is ambiguous (mixin dispatch, conditional |
| 111 | + use, metaprogramming). |
| 112 | +- <0.4 and/or \`unclear\`: source is generated/absent, or indirection you could not resolve. |
| 113 | +
|
| 114 | +Report evidence as exact file paths, line numbers, and short verbatim snippets.` |
| 115 | +} |
| 116 | + |
| 117 | +export const RUBYGEMS_REACHABILITY_PROMPT = |
| 118 | + 'Analyze this package per your instructions and produce the structured verdict. ' + |
| 119 | + 'Start by listing the project structure and grepping for the import signatures.' |
0 commit comments