Repository navigation
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 WalkthroughWalkthrough
ChangesRaw WHERE Bind Substitution
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to Raw where/having fragments no longer let one inserted value affect a later placeholder, and the change adds test coverage and CI routing for this. No merge-blocking risk was found. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change strengthens separation between SQL and supplied values without adding database authority or new callers. No introduced security defect was established. Assurance remains limited across other runtimes, custom adapters, and application-specific usage. Retained concerns Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
cda6645 to
85b9336
Compare
Fold rubys#459 (limit/offset/hash order as values) plus string order parsed as col/table.col with ASC/DESC, and validate last_n/first_n before mutating. Fold rubys#464 positional where/having binds as a one-pass split so ? and backslashes in values stay data. Keep main's named-bind dispatch and nested-hash order (campfire sidebar). Sanitize head/render locations and headers[]= with a shared HeaderStore (Puma drop). Share one HttpHeaders helper for Tep/CGI. Escape CR/LF/NUL in URL filename components; re-allowlist disk disposition at show. redirect_to refuses an absolute URL whose host is not this request's Host. Session/flash cookies get SameSite=Lax and Secure on HTTPS; signed jar options are honored. HMAC-only session store remains a named residual (no AES-GCM in this runtime). CSRF: verify_authenticity_token on shared Base; empty session token matches nothing. Write-once in runtime/ruby, not a close across targets. Overlay/spinel still mint per-session tokens. References #23. Pin with emit_and_run overlays. Skip ActionText XSS, LIKE, path decode, Tep parser PRs, and Campfire app patches. Co-authored-by: Thomas Klemm <github@tklemm.eu>
Split the original fragment once and interleave adapter-escaped values. Preserve unfilled placeholders and keep Array escaping as on main; no bind lowering, statement caching, or adapter behavior changes. Keep the single-Hash named-bind dispatch from 12bf080 verbatim. Named binds already scan the original fragment once; their helper and character tests are unchanged. Only the positional loop is replaced. Port the raw WHERE regression from 8b26e4bd without its Array-list or cache changes. Check scalar values, quotes, replacement escapes, negation, HAVING and argument-count edges on emitted Ruby and Spinel. Route changes to the new probe through the existing native bind suite. Rebase 85b9336 onto main c49721b. Keep main's current committed inference tracker and ceiling history, including canonical class-ID lookup, dependency RBS and cross-stem ivar merging. Measure main and the rebased source locally with the same locked inference probe and --test-threads=1 --nocapture: 0 -> 0 unresolved sites (relation.rb 0 -> 0), three tests pass on each. The measured delta is 0 - 0 = 0. Apply only that delta to main's committed ceiling: 0 + (0 - 0) = 0. The former +4 was measured with the older probe on 0ac82f5; do not carry its 1404 ceiling forward. The full-context gate still requires zero unresolved types; no RBS signature changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85b9336 to
30b8f26
Compare
|
Superseded by #476 (merge One-pass positional Closing this PR; no remaining material from this patch is missing on |
Values passed to raw SQL fragments, such as
where("name = ? AND role = ?", a, b), now stay inside their quotes. Before this, a value containing?let the next value be inserted outside its quotes, so crafted input could change the meaning of the query.Reported privately to the maintainers first; Sam preferred open disclosure, since Roundhouse hasn't been security-audited and isn't widely deployed. Thanks to Greg Molnar for reviewing the report and the patch.
With a crafted second value, the same mechanism turned a query that should match no rows into one that returned every row, on CRuby and on native Spinel. Rails returns no rows for the same input.
Cause.
Relation#substitute_binds(relation.rb:1824–1830) filled placeholders one at a time withString#sub, and each call searched the string built so far, including values already inserted.subalso treats backslashes in its replacement string specially, so a value such aspath\1lost its backslash and matched the wrong row.Fix. Split the original fragment on
?once and interleave the escaped values, asActiveRecord::Base.sanitize_sqlalready does. Inserted values are never searched again, so question marks and backslashes inside them stay data. Missing arguments still leave their placeholders, and Array arguments are escaped as before.Named binds (
:namewith a Hash), added in 12bf080, already scan the original fragment once and are unchanged.Affected. The Ruby, JRuby and Spinel targets, which share this runtime:
where,where!andwhere.notwith a fragment and two or more values (where, not, add_condition),having(having), and scopes that pass their arguments into such a fragment. The param-binds setting makes no difference, because raw fragments are substituted inline either way.Tests.
raw_where_substitution_rubyandraw_where_substitution_spinelrun real queries covering question marks, backslashes and replacement escapes, quoting, negation, HAVING, Arrays and argument-count edges. Both fail onmainwithscalar value consumed later placeholderand pass with this change.Validation on main
c49721be:cargo test --locked --lib969 passed; the bind suite 6 passed (3 Ruby, 3 native Spinel); runtime integration 5 passed; the CI planner 50 passed. Emitted Ruby, JRuby and Spinel trees change only insubstitute_binds. The RBS inference tracker reports no new unresolved sites (0 onmainand 0 with this change).This complements #459, which casts
limit,offsetandorder; both editrelation.rband the typing test, so whichever merges second needs a small rebase. #403 (draft) carries an equivalent change and will drop it once this merges.Part of the Conduit worklist: bunnykong#1
🤖 Generated with Claude Code
Summary by CodeRabbit
where,not, andhavingclauses now substitute values in placeholder order. Question marks inside values are preserved rather than treated as additional placeholders.