Skip to content

Commit 38955d1

Browse files
committed
Ruby: port part of the Rails model
1 parent 9f59b6b commit 38955d1

1 file changed

Lines changed: 43 additions & 87 deletions

File tree

ruby/ql/lib/codeql/ruby/frameworks/Rails.qll

Lines changed: 43 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -92,102 +92,63 @@ module Rails {
9292
}
9393

9494
/**
95-
* A reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
95+
* Gets a reference to the `Rails` constant.
96+
*/
97+
private DataFlow::ConstRef rails() { result = DataFlow::getConst("Rails") }
98+
99+
/**
100+
* Gets a reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
96101
* `Engine` and `Application` extend `Railtie`, but may not have definitions present in the database.
97102
*/
98-
private class RailtieClassAccess extends ConstantReadAccess {
99-
RailtieClassAccess() {
100-
this.getScopeExpr().(ConstantAccess).getName() = "Rails" and
101-
this.getName() = ["Railtie", "Engine", "Application"]
102-
}
103+
private DataFlow::ConstRef railtie() {
104+
result = rails().getConst(["Railtie", "Engine", "Application"])
103105
}
104106

105-
// A `ClassDeclaration` that (transitively) extends `Rails::Railtie`
106-
private class RailtieClass extends ClassDeclaration {
107-
RailtieClass() {
108-
this.getSuperclassExpr() instanceof RailtieClassAccess or
109-
exists(RailtieClass other |
110-
other.getModule() = this.getSuperclassExpr().(ConstantReadAccess).getModule()
111-
)
112-
}
113-
}
107+
/** Gets a class that transitively extends `Rails::Railtie` */
108+
private DataFlow::ClassNode railtieClass() { result = railtie().getADescendentModule() }
114109

115-
private DataFlow::CallNode getAConfigureCallNode() {
116-
// `Rails.application.configure`
117-
result = API::getTopLevelMember("Rails").getReturn("application").getAMethodCall("configure")
110+
/**
111+
* Gets a reference to `Rails::Application` or `Rails.application`.
112+
*/
113+
private DataFlow::LocalSourceNode railsApp() {
114+
result = rails().getAMethodCall("application")
118115
or
119-
// `Rails::Application.configure`
120-
exists(ConstantReadAccess read, RailtieClass cls |
121-
read = result.getReceiver().asExpr().getExpr() and
122-
read.getModule() = cls.getModule() and
123-
result.asExpr().getExpr().(MethodCall).getMethodName() = "configure"
124-
)
116+
result = rails().getConst("Application")
125117
}
126118

127119
/**
128120
* Classes representing accesses to the Rails config object.
129121
*/
130122
private module Config {
131-
/**
132-
* An access to a Rails config object.
133-
*/
134-
private class SourceNode extends DataFlow::LocalSourceNode {
135-
SourceNode() {
136-
// `Foo < Rails::Application ... config ...`
137-
exists(MethodCall configCall | this.asExpr().getExpr() = configCall |
138-
configCall.getMethodName() = "config" and
139-
configCall.getEnclosingModule() instanceof RailtieClass
140-
)
141-
or
142-
// `Rails.application.config`
143-
this = API::getTopLevelMember("Rails").getReturn("application").getReturn("config").asSource()
144-
or
145-
// `Rails.application.configure { ... config ... }`
146-
// `Rails::Application.configure { ... config ... }`
147-
exists(DataFlow::CallNode configureCallNode, Block block, MethodCall configCall |
148-
configCall = this.asExpr().getExpr()
149-
|
150-
configureCallNode = getAConfigureCallNode() and
151-
block = configureCallNode.asExpr().getExpr().(MethodCall).getBlock() and
152-
configCall.getParent+() = block and
153-
configCall.getMethodName() = "config"
154-
)
155-
}
156-
}
157-
158-
/**
159-
* A reference to the Rails config object.
160-
*/
161-
class Node extends DataFlow::Node {
162-
Node() { exists(SourceNode src | src.flowsTo(this)) }
123+
DataFlow::LocalSourceNode configSource() {
124+
// `Foo < Rails::Application ... config ...`
125+
result = railtieClass().getAnOwnModuleSelf().getAMethodCall("config")
126+
or
127+
// `Rails.application.config`
128+
result = railsApp().getAMethodCall("config")
129+
or
130+
// TODO: move away from getParent+() when have better infrastructure for overridden 'self' in blocks
131+
// `Rails.application.configure { ... config ... }`
132+
// `Rails::Application.configure { ... config ... }`
133+
exists(Block block, MethodCall configCall | configCall = result.asExpr().getExpr() |
134+
block = railsApp().getAMethodCall("configure").getBlock().asExpr().getExpr() and
135+
configCall.getParent+() = block and
136+
configCall.getMethodName() = "config"
137+
)
163138
}
164139

165140
/**
166-
* A reference to the ActionController config object.
141+
* Gets a reference to the ActionController config object.
167142
*/
168-
class ActionControllerNode extends DataFlow::Node {
169-
ActionControllerNode() {
170-
exists(DataFlow::CallNode source |
171-
source.getReceiver() instanceof Node and
172-
source.getMethodName() = "action_controller"
173-
|
174-
source.flowsTo(this)
175-
)
176-
}
143+
DataFlow::LocalSourceNode actionController() {
144+
result = configSource().getAMethodCall("action_controller")
177145
}
178146

179147
/**
180-
* A reference to the ActionDispatch config object.
148+
* Gets a reference to the ActionDispatch config object.
181149
*/
182-
class ActionDispatchNode extends DataFlow::Node {
183-
ActionDispatchNode() {
184-
exists(DataFlow::CallNode source |
185-
source.getReceiver() instanceof Node and
186-
source.getMethodName() = "action_dispatch"
187-
|
188-
source.flowsTo(this)
189-
)
190-
}
150+
DataFlow::LocalSourceNode actionDispatch() {
151+
result = configSource().getAMethodCall("action_dispatch")
191152
}
192153
}
193154

@@ -200,12 +161,11 @@ private module Settings {
200161
loc.getFile().getStem() = "test"
201162
}
202163

203-
private class Setting extends DataFlow::CallNode {
164+
private class Setting extends DataFlow::SetterCallNode {
204165
Setting() {
205166
// exclude some test configuration
206167
not isInTestConfiguration(this.getLocation()) and
207-
this.getReceiver+() instanceof Config::Node and
208-
this.asExpr().getExpr() instanceof SetterMethodCall
168+
this = Config::configSource().getAMethodCall+()
209169
}
210170
}
211171

@@ -257,8 +217,7 @@ private module Settings {
257217
private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
258218
CsrfProtectionSetting::Range {
259219
AllowForgeryProtectionSetting() {
260-
this.getReceiver() instanceof Config::ActionControllerNode and
261-
this.getMethodName() = "allow_forgery_protection="
220+
this = Config::actionController().getAMethodCall("allow_forgery_protection=")
262221
}
263222

264223
override boolean getVerificationSetting() { result = this.getValue() }
@@ -272,8 +231,7 @@ private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
272231
private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
273232
CookieSecurityConfigurationSetting::Range {
274233
EncryptedCookieCipherSetting() {
275-
this.getReceiver() instanceof Config::ActionDispatchNode and
276-
this.getMethodName() = "encrypted_cookie_cipher="
234+
this = Config::actionDispatch().getAMethodCall("encrypted_cookie_cipher=")
277235
}
278236

279237
OpenSslCipher getCipher() { this.getValueText() = result.getName() }
@@ -293,8 +251,7 @@ private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
293251
private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanSetting,
294252
CookieSecurityConfigurationSetting::Range {
295253
UseAuthenticatedCookieEncryptionSetting() {
296-
this.getReceiver() instanceof Config::ActionDispatchNode and
297-
this.getMethodName() = "use_authenticated_cookie_encryption="
254+
this = Config::actionDispatch().getAMethodCall("use_authenticated_cookie_encryption=")
298255
}
299256

300257
boolean getDefaultValue() { result = true }
@@ -316,8 +273,7 @@ private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanS
316273
private class CookiesSameSiteProtectionSetting extends Settings::NillableStringlikeSetting,
317274
CookieSecurityConfigurationSetting::Range {
318275
CookiesSameSiteProtectionSetting() {
319-
this.getReceiver() instanceof Config::ActionDispatchNode and
320-
this.getMethodName() = "cookies_same_site_protection="
276+
this = Config::actionDispatch().getAMethodCall("cookies_same_site_protection=")
321277
}
322278

323279
string getDefaultValue() { result = "lax" }

0 commit comments

Comments
 (0)