mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge branch 'github:main' into main
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
## 0.5.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Ruby 3.1: one-line pattern matches are now supported. The AST nodes are named `TestPattern` (`expr in pattern`) and `MatchPattern` (`expr => pattern`).
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Calls to `ApplicationController#render` and `ApplicationController::Renderer#render` are recognized as Rails rendering calls.
|
||||
4
ruby/ql/lib/change-notes/2023-02-03-twirp.md
Normal file
4
ruby/ql/lib/change-notes/2023-02-03-twirp.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Support for [Twirp framework](https://twitchtv.github.io/twirp/docs/intro.html).
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.5.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Ruby 3.1: one-line pattern matches are now supported. The AST nodes are named `TestPattern` (`expr in pattern`) and `MatchPattern` (`expr => pattern`).
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.2
|
||||
lastReleaseVersion: 0.5.3
|
||||
|
||||
@@ -27,3 +27,4 @@ private import codeql.ruby.frameworks.ActionDispatch
|
||||
private import codeql.ruby.frameworks.PosixSpawn
|
||||
private import codeql.ruby.frameworks.StringFormatters
|
||||
private import codeql.ruby.frameworks.Json
|
||||
private import codeql.ruby.frameworks.Twirp
|
||||
|
||||
@@ -364,6 +364,21 @@ private class ActionControllerRenderToCall extends RenderToCallImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `ActionController::Renderer#render`. */
|
||||
private class RendererRenderCall extends RenderCallImpl {
|
||||
RendererRenderCall() {
|
||||
this =
|
||||
[
|
||||
// ActionController#render is an alias for ActionController::Renderer#render
|
||||
any(ActionControllerClass c).getAnImmediateReference().getAMethodCall("render"),
|
||||
any(ActionControllerClass c)
|
||||
.getAnImmediateReference()
|
||||
.getAMethodCall("renderer")
|
||||
.getAMethodCall("render")
|
||||
].asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `html_escape` from within a controller. */
|
||||
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
|
||||
ActionControllerHtmlEscapeCall() {
|
||||
|
||||
85
ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll
Normal file
85
ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Provides classes for modeling the `Twirp` framework.
|
||||
*/
|
||||
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.AST as Ast
|
||||
private import codeql.ruby.security.ServerSideRequestForgeryCustomizations
|
||||
private import codeql.ruby.Concepts
|
||||
|
||||
/**
|
||||
* Provides classes for modeling the `Twirp` framework.
|
||||
*/
|
||||
module Twirp {
|
||||
/**
|
||||
* A Twirp service instantiation
|
||||
*/
|
||||
class ServiceInstantiation extends DataFlow::CallNode {
|
||||
ServiceInstantiation() {
|
||||
this =
|
||||
API::getTopLevelMember("Twirp").getMember("Service").getASubclass().getAnInstantiation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a local source node for the Service instantiation argument (the service handler).
|
||||
*/
|
||||
private DataFlow::LocalSourceNode getHandlerSource() {
|
||||
result = this.getArgument(0).getALocalSource()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API::Node for the service handler's class.
|
||||
*/
|
||||
private API::Node getAHandlerClassApiNode() {
|
||||
result.getAnInstantiation() = this.getHandlerSource()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the AST module for the service handler's class.
|
||||
*/
|
||||
private Ast::Module getAHandlerClassAstNode() {
|
||||
result =
|
||||
this.getAHandlerClassApiNode()
|
||||
.asSource()
|
||||
.asExpr()
|
||||
.(CfgNodes::ExprNodes::ConstantReadAccessCfgNode)
|
||||
.getExpr()
|
||||
.getModule()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a handler's method.
|
||||
*/
|
||||
Ast::Method getAHandlerMethod() {
|
||||
result = this.getAHandlerClassAstNode().getAnInstanceMethod()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Twirp client
|
||||
*/
|
||||
class ClientInstantiation extends DataFlow::CallNode {
|
||||
ClientInstantiation() {
|
||||
this = API::getTopLevelMember("Twirp").getMember("Client").getASubclass().getAnInstantiation()
|
||||
}
|
||||
}
|
||||
|
||||
/** The URL of a Twirp service, considered as a sink. */
|
||||
class ServiceUrlAsSsrfSink extends ServerSideRequestForgery::Sink {
|
||||
ServiceUrlAsSsrfSink() { exists(ClientInstantiation c | c.getArgument(0) = this) }
|
||||
}
|
||||
|
||||
/** A parameter that will receive parts of the url when handling an incoming request. */
|
||||
class UnmarshaledParameter extends Http::Server::RequestInputAccess::Range,
|
||||
DataFlow::ParameterNode {
|
||||
UnmarshaledParameter() {
|
||||
exists(ServiceInstantiation i | i.getAHandlerMethod().getParameter(0) = this.asParameter())
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "Twirp Unmarhaled Parameter" }
|
||||
|
||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::bodyInputKind() }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-all
|
||||
version: 0.5.3-dev
|
||||
version: 0.5.4-dev
|
||||
groups: ruby
|
||||
extractor: ruby
|
||||
dbscheme: ruby.dbscheme
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
## 0.5.3
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `rb/regex/badly-anchored-regexp`, to detect regular expression validators that use `^` and `$`
|
||||
as anchors and therefore might match only a single line of a multi-line string.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `rb/polynomial-redos` query now considers the entrypoints of the API of a gem as sources.
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `rb/polynomial-redos` query now considers the entrypoints of the API of a gem as sources.
|
||||
@@ -1,5 +1,10 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
## 0.5.3
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `rb/regex/badly-anchored-regexp`, to detect regular expression validators that use `^` and `$`
|
||||
as anchors and therefore might match only a single line of a multi-line string.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `rb/polynomial-redos` query now considers the entrypoints of the API of a gem as sources.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.2
|
||||
lastReleaseVersion: 0.5.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-queries
|
||||
version: 0.5.3-dev
|
||||
version: 0.5.4-dev
|
||||
groups:
|
||||
- ruby
|
||||
- queries
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn()
|
||||
Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn()
|
||||
a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn()
|
||||
b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn()
|
||||
end
|
||||
end #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn()
|
||||
|
||||
Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something").getMethod("withNamedArg").getReturn()
|
||||
Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something")
|
||||
a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn()
|
||||
b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn()
|
||||
end
|
||||
end #$ use=getMember("Something").getMethod("withNamedArg").getReturn()
|
||||
|
||||
Something.withLambda ->(a, b) { #$ use=getMember("Something").getMethod("withLambda").getReturn()
|
||||
Something.withLambda ->(a, b) { #$ use=getMember("Something")
|
||||
a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn()
|
||||
}
|
||||
} #$ use=getMember("Something").getMethod("withLambda").getReturn()
|
||||
|
||||
Something.namedCallback( #$ use=getMember("Something").getMethod("namedCallback").getReturn()
|
||||
Something.namedCallback( #$ use=getMember("Something")
|
||||
onEvent: ->(a, b) {
|
||||
a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn()
|
||||
}
|
||||
)
|
||||
) #$ use=getMember("Something").getMethod("namedCallback").getReturn()
|
||||
|
||||
Something.nestedCall1 do |a| #$ use=getMember("Something").getMethod("nestedCall1").getReturn()
|
||||
a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn()
|
||||
Something.nestedCall1 do |a| #$ use=getMember("Something")
|
||||
a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0)
|
||||
b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn()
|
||||
end
|
||||
end
|
||||
end #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn()
|
||||
end #$ use=getMember("Something").getMethod("nestedCall1").getReturn()
|
||||
|
||||
def getCallback()
|
||||
->(x) {
|
||||
@@ -33,7 +33,7 @@ def getCallback()
|
||||
end
|
||||
Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn()
|
||||
|
||||
Something.withMixed do |a, *args, b| #$ use=getMember("Something").getMethod("withMixed").getReturn()
|
||||
Something.withMixed do |a, *args, b| #$ use=getMember("Something")
|
||||
a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn()
|
||||
# b.something # not currently handled correctly
|
||||
end
|
||||
end #$ use=getMember("Something").getMethod("withMixed").getReturn()
|
||||
|
||||
@@ -13,9 +13,9 @@ Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMeth
|
||||
Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn()
|
||||
Const.each do |c| #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock()
|
||||
Const.each do |c| #$ use=getMember("Const")
|
||||
puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0) use=getMember("Const").getContent(element)
|
||||
end
|
||||
end #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock()
|
||||
|
||||
foo = Foo #$ use=getMember("Foo")
|
||||
foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
@@ -44,7 +44,7 @@ class ApiUseTest extends InlineExpectationsTest {
|
||||
max(API::Node a2, Location l2, DataFlow::Node n2 |
|
||||
relevantNode(a2, n2, l2, tag) and
|
||||
l2.getFile() = location.getFile() and
|
||||
l2.getStartLine() = location.getStartLine()
|
||||
l2.getEndLine() = location.getEndLine()
|
||||
|
|
||||
a2.getPath()
|
||||
order by
|
||||
|
||||
@@ -44,9 +44,9 @@ end
|
||||
|
||||
def m8
|
||||
sink(s8 { source "a" }) # $ hasValueFlow=a
|
||||
sink(s8 do # $hasValueFlow=a
|
||||
sink(s8 do
|
||||
source "a"
|
||||
end)
|
||||
end) # $hasValueFlow=a
|
||||
end
|
||||
|
||||
def m9
|
||||
|
||||
5
ruby/ql/test/library-tests/frameworks/Twirp/Gemfile
Normal file
5
ruby/ql/test/library-tests/frameworks/Twirp/Gemfile
Normal file
@@ -0,0 +1,5 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "rack"
|
||||
gem "webrick"
|
||||
gem "twirp"
|
||||
@@ -0,0 +1,6 @@
|
||||
sourceTest
|
||||
| hello_world_server.rb:8:13:8:15 | req |
|
||||
ssrfSinkTest
|
||||
| hello_world_client.rb:6:47:6:75 | "http://localhost:8080/twirp" |
|
||||
serviceInstantiationTest
|
||||
| hello_world_server.rb:24:11:24:61 | call to new |
|
||||
8
ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql
Normal file
8
ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql
Normal file
@@ -0,0 +1,8 @@
|
||||
private import codeql.ruby.frameworks.Twirp
|
||||
private import codeql.ruby.DataFlow
|
||||
|
||||
query predicate sourceTest(Twirp::UnmarshaledParameter source) { any() }
|
||||
|
||||
query predicate ssrfSinkTest(Twirp::ServiceUrlAsSsrfSink sink) { any() }
|
||||
|
||||
query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() }
|
||||
@@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
package example.hello_world;
|
||||
|
||||
|
||||
service HelloWorld {
|
||||
rpc Hello(HelloRequest) returns (HelloResponse);
|
||||
}
|
||||
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message HelloResponse {
|
||||
string message = 1;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: hello_world/service.proto
|
||||
|
||||
require 'google/protobuf'
|
||||
|
||||
Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
add_file("hello_world/service.proto", :syntax => :proto3) do
|
||||
add_message "example.hello_world.HelloRequest" do
|
||||
optional :name, :string, 1
|
||||
end
|
||||
add_message "example.hello_world.HelloResponse" do
|
||||
optional :message, :string, 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Example
|
||||
module HelloWorld
|
||||
HelloRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("example.hello_world.HelloRequest").msgclass
|
||||
HelloResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("example.hello_world.HelloResponse").msgclass
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
# Code generated by protoc-gen-twirp_ruby 1.10.0, DO NOT EDIT.
|
||||
require 'twirp'
|
||||
require_relative 'service_pb.rb'
|
||||
|
||||
module Example
|
||||
module HelloWorld
|
||||
class HelloWorldService < ::Twirp::Service
|
||||
package 'example.hello_world'
|
||||
service 'HelloWorld'
|
||||
rpc :Hello, HelloRequest, HelloResponse, :ruby_method => :hello
|
||||
end
|
||||
|
||||
class HelloWorldClient < ::Twirp::Client
|
||||
client_for HelloWorldService
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
require 'rack'
|
||||
|
||||
require_relative 'hello_world/service_twirp.rb'
|
||||
|
||||
# test: ssrfSink
|
||||
c = Example::HelloWorld::HelloWorldClient.new("http://localhost:8080/twirp")
|
||||
|
||||
resp = c.hello(name: "World")
|
||||
if resp.error
|
||||
puts resp.error
|
||||
else
|
||||
puts resp.data.message
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
require 'rack'
|
||||
require 'webrick'
|
||||
|
||||
require_relative 'hello_world/service_twirp.rb'
|
||||
|
||||
class HelloWorldHandler
|
||||
# test: request
|
||||
def hello(req, env)
|
||||
puts ">> Hello #{req.name}"
|
||||
{message: "Hello #{req.name}"}
|
||||
end
|
||||
end
|
||||
|
||||
class FakeHelloWorldHandler
|
||||
# test: !request
|
||||
def hello(req, env)
|
||||
puts ">> Hello #{req.name}"
|
||||
{message: "Hello #{req.name}"}
|
||||
end
|
||||
end
|
||||
|
||||
handler = HelloWorldHandler.new()
|
||||
# test: serviceInstantiation
|
||||
service = Example::HelloWorld::HelloWorldService.new(handler)
|
||||
|
||||
path_prefix = "/twirp/" + service.full_name
|
||||
server = WEBrick::HTTPServer.new(Port: 8080)
|
||||
server.mount path_prefix, Rack::Handler::WEBrick, service
|
||||
server.start
|
||||
@@ -3,7 +3,7 @@ actionControllerControllerClasses
|
||||
| controllers/comments_controller.rb:1:1:104:3 | CommentsController |
|
||||
| controllers/foo/bars_controller.rb:3:1:46:3 | BarsController |
|
||||
| controllers/photos_controller.rb:1:1:10:3 | PhotosController |
|
||||
| controllers/posts_controller.rb:1:1:30:3 | PostsController |
|
||||
| controllers/posts_controller.rb:1:1:32:3 | PostsController |
|
||||
| controllers/tags_controller.rb:1:1:2:3 | TagsController |
|
||||
| controllers/users/notifications_controller.rb:2:3:5:5 | Users::NotificationsController |
|
||||
| input_access.rb:1:1:50:3 | UsersController |
|
||||
@@ -23,9 +23,9 @@ actionControllerActionMethods
|
||||
| controllers/foo/bars_controller.rb:34:3:39:5 | show_2 |
|
||||
| controllers/photos_controller.rb:3:3:6:5 | show |
|
||||
| controllers/photos_controller.rb:8:3:9:5 | foo |
|
||||
| controllers/posts_controller.rb:12:3:13:5 | index |
|
||||
| controllers/posts_controller.rb:15:3:16:5 | show |
|
||||
| controllers/posts_controller.rb:18:3:19:5 | upvote |
|
||||
| controllers/posts_controller.rb:12:3:15:5 | index |
|
||||
| controllers/posts_controller.rb:17:3:18:5 | show |
|
||||
| controllers/posts_controller.rb:20:3:21:5 | upvote |
|
||||
| controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
|
||||
| input_access.rb:2:3:49:5 | index |
|
||||
| logging.rb:2:5:8:7 | index |
|
||||
@@ -71,7 +71,7 @@ paramsCalls
|
||||
| controllers/foo/bars_controller.rb:14:10:14:15 | call to params |
|
||||
| controllers/foo/bars_controller.rb:21:21:21:26 | call to params |
|
||||
| controllers/foo/bars_controller.rb:22:10:22:15 | call to params |
|
||||
| controllers/posts_controller.rb:24:23:24:28 | call to params |
|
||||
| controllers/posts_controller.rb:26:23:26:28 | call to params |
|
||||
| params_flow.rb:3:10:3:15 | call to params |
|
||||
| params_flow.rb:7:10:7:15 | call to params |
|
||||
| params_flow.rb:11:10:11:15 | call to params |
|
||||
@@ -126,7 +126,7 @@ paramsSources
|
||||
| controllers/foo/bars_controller.rb:14:10:14:15 | call to params |
|
||||
| controllers/foo/bars_controller.rb:21:21:21:26 | call to params |
|
||||
| controllers/foo/bars_controller.rb:22:10:22:15 | call to params |
|
||||
| controllers/posts_controller.rb:24:23:24:28 | call to params |
|
||||
| controllers/posts_controller.rb:26:23:26:28 | call to params |
|
||||
| params_flow.rb:3:10:3:15 | call to params |
|
||||
| params_flow.rb:7:10:7:15 | call to params |
|
||||
| params_flow.rb:11:10:11:15 | call to params |
|
||||
@@ -191,7 +191,7 @@ httpInputAccesses
|
||||
| controllers/foo/bars_controller.rb:14:10:14:15 | call to params | ActionController::Metal#params |
|
||||
| controllers/foo/bars_controller.rb:21:21:21:26 | call to params | ActionController::Metal#params |
|
||||
| controllers/foo/bars_controller.rb:22:10:22:15 | call to params | ActionController::Metal#params |
|
||||
| controllers/posts_controller.rb:24:23:24:28 | call to params | ActionController::Metal#params |
|
||||
| controllers/posts_controller.rb:26:23:26:28 | call to params | ActionController::Metal#params |
|
||||
| input_access.rb:3:5:3:18 | call to params | ActionDispatch::Request#params |
|
||||
| input_access.rb:4:5:4:22 | call to parameters | ActionDispatch::Request#parameters |
|
||||
| input_access.rb:5:5:5:15 | call to GET | ActionDispatch::Request#GET |
|
||||
@@ -297,6 +297,9 @@ renderCalls
|
||||
| controllers/foo/bars_controller.rb:35:5:35:33 | call to render |
|
||||
| controllers/foo/bars_controller.rb:38:5:38:50 | call to render |
|
||||
| controllers/foo/bars_controller.rb:44:5:44:17 | call to render |
|
||||
| controllers/posts_controller.rb:13:5:13:51 | call to render |
|
||||
| controllers/posts_controller.rb:14:5:14:127 | call to render |
|
||||
| controllers/posts_controller.rb:36:5:36:51 | call to render |
|
||||
httpResponses
|
||||
| controllers/comments_controller.rb:26:5:26:17 | call to body= | controllers/comments_controller.rb:26:21:26:34 | ... = ... |
|
||||
| controllers/comments_controller.rb:36:5:36:37 | call to send_file | controllers/comments_controller.rb:36:24:36:36 | "my-file.ext" |
|
||||
|
||||
@@ -42,12 +42,12 @@
|
||||
| controllers/comments_controller.rb:68:3:70:5 | destroy | controllers/comments_controller.rb:102:3:103:5 | bar | controllers/comments_controller.rb:68:3:70:5 | destroy |
|
||||
| controllers/photos_controller.rb:3:3:6:5 | show | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/photos_controller.rb:3:3:6:5 | show |
|
||||
| controllers/photos_controller.rb:3:3:6:5 | show | controllers/photos_controller.rb:3:3:6:5 | show | controllers/photos_controller.rb:8:3:9:5 | foo |
|
||||
| controllers/posts_controller.rb:12:3:13:5 | index | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:12:3:13:5 | index |
|
||||
| controllers/posts_controller.rb:12:3:13:5 | index | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user |
|
||||
| controllers/posts_controller.rb:15:3:16:5 | show | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:15:3:16:5 | show |
|
||||
| controllers/posts_controller.rb:15:3:16:5 | show | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/posts_controller.rb:23:3:25:5 | set_post |
|
||||
| controllers/posts_controller.rb:15:3:16:5 | show | controllers/posts_controller.rb:23:3:25:5 | set_post | controllers/application_controller.rb:6:3:8:5 | set_user |
|
||||
| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:18:3:19:5 | upvote |
|
||||
| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/posts_controller.rb:23:3:25:5 | set_post |
|
||||
| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/posts_controller.rb:27:3:29:5 | log_upvote |
|
||||
| controllers/posts_controller.rb:18:3:19:5 | upvote | controllers/posts_controller.rb:23:3:25:5 | set_post | controllers/application_controller.rb:6:3:8:5 | set_user |
|
||||
| controllers/posts_controller.rb:12:3:15:5 | index | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:12:3:15:5 | index |
|
||||
| controllers/posts_controller.rb:12:3:15:5 | index | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/application_controller.rb:6:3:8:5 | set_user |
|
||||
| controllers/posts_controller.rb:17:3:18:5 | show | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:17:3:18:5 | show |
|
||||
| controllers/posts_controller.rb:17:3:18:5 | show | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/posts_controller.rb:25:3:27:5 | set_post |
|
||||
| controllers/posts_controller.rb:17:3:18:5 | show | controllers/posts_controller.rb:25:3:27:5 | set_post | controllers/application_controller.rb:6:3:8:5 | set_user |
|
||||
| controllers/posts_controller.rb:20:3:21:5 | upvote | controllers/application_controller.rb:6:3:8:5 | set_user | controllers/posts_controller.rb:20:3:21:5 | upvote |
|
||||
| controllers/posts_controller.rb:20:3:21:5 | upvote | controllers/application_controller.rb:10:3:12:5 | log_request | controllers/posts_controller.rb:25:3:27:5 | set_post |
|
||||
| controllers/posts_controller.rb:20:3:21:5 | upvote | controllers/posts_controller.rb:20:3:21:5 | upvote | controllers/posts_controller.rb:29:3:31:5 | log_upvote |
|
||||
| controllers/posts_controller.rb:20:3:21:5 | upvote | controllers/posts_controller.rb:25:3:27:5 | set_post | controllers/application_controller.rb:6:3:8:5 | set_user |
|
||||
|
||||
@@ -10,6 +10,8 @@ class PostsController < ApplicationController
|
||||
before_action :set_user
|
||||
|
||||
def index
|
||||
PostsController.render(template: "posts/index")
|
||||
PostsController.renderer.render(template: "posts/index", locals: { show_full_post: true }, assigns: { @posts => Post.all })
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -28,3 +30,10 @@ class PostsController < ApplicationController
|
||||
Rails.logger.info("Post upvoted: #{@post.id}")
|
||||
end
|
||||
end
|
||||
|
||||
class NotAController
|
||||
def foo
|
||||
PostsController.render(template: "posts/index")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -47,54 +47,54 @@ def m6(arg1, arg2)
|
||||
end
|
||||
|
||||
# Bad: method has parameter but only one result is memoized.
|
||||
def m7(arg) # $result=BAD
|
||||
def m7(arg)
|
||||
@m7 ||= begin
|
||||
arg += 3
|
||||
end
|
||||
@m7
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Bad: method has parameter but only one result is memoized.
|
||||
def m8(arg) # $result=BAD
|
||||
def m8(arg)
|
||||
@m8 ||= begin
|
||||
long_running_method(arg)
|
||||
end
|
||||
@m8
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Bad: method has parameter but only one result is memoized.
|
||||
def m9(arg) # $result=BAD
|
||||
def m9(arg)
|
||||
@m9 ||= long_running_method(arg)
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Bad: method has parameter but only one result is memoized.
|
||||
def m10(arg1, arg2) # $result=BAD
|
||||
def m10(arg1, arg2)
|
||||
@m10 ||= long_running_method(arg1, arg2)
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Bad: `arg2` not used in key.
|
||||
def m11(arg1, arg2) # $result=BAD
|
||||
def m11(arg1, arg2)
|
||||
@m11 ||= {}
|
||||
@m11[arg1] ||= long_running_method(arg1, arg2)
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Bad: `arg2` not used in key.
|
||||
def m12(arg1, arg2) # $result=BAD
|
||||
def m12(arg1, arg2)
|
||||
@m12 ||= Hash.new do |h1, arg1|
|
||||
h1[arg1] = result(arg1, arg2)
|
||||
end
|
||||
@m12[arg1]
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Bad: arg not used in key.
|
||||
def m13(id:) # $result=BAD
|
||||
def m13(id:)
|
||||
@m13 ||= Rails.cache.fetch("product_sku/#{id}", expires_in: 30.minutes) do
|
||||
ActiveRecord::Base.transaction do
|
||||
ProductSku.find_by(id: id)
|
||||
end
|
||||
end
|
||||
@m13
|
||||
end
|
||||
end # $result=BAD
|
||||
|
||||
# Good (FP): arg is used in key via string interpolation.
|
||||
def m14(arg)
|
||||
|
||||
@@ -80,9 +80,9 @@ def m9(x)
|
||||
x = x.gsub(/^(\.\.\/?)+/, "") # OK
|
||||
|
||||
# NOT OK
|
||||
x = x.gsub(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/) do |match| # $ hasResult=html
|
||||
x = x.gsub(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/) do |match|
|
||||
if unknown then match else "" end
|
||||
end
|
||||
end # $ hasResult=html
|
||||
|
||||
x = x.gsub(/<\/?([a-z][a-z0-9]*)\b[^>]*>/i, "") # NOT OK [INCONSISTENCY] $ hasResult=html
|
||||
|
||||
@@ -113,10 +113,10 @@ def m9(x)
|
||||
|
||||
x = x.gsub(/<!\-\-DEVEL[\d\D]*?DEVEL\-\->/, "") # OK
|
||||
|
||||
x = x # $ hasResult=path
|
||||
x = x
|
||||
.gsub(/^\.\//, "")
|
||||
.gsub(/\/\.\//, "/")
|
||||
.gsub(/[^\/]*\/\.\.\//, "")
|
||||
.gsub(/[^\/]*\/\.\.\//, "") # $ hasResult=path
|
||||
|
||||
x
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user