Merge branch 'github:main' into main

This commit is contained in:
Grzegorz Niedziela
2023-02-23 10:50:15 +00:00
committed by GitHub
395 changed files with 31400 additions and 2624 deletions

View File

@@ -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

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Calls to `ApplicationController#render` and `ApplicationController::Renderer#render` are recognized as Rails rendering calls.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Support for [Twirp framework](https://twitchtv.github.io/twirp/docs/intro.html).

View File

@@ -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`).

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -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

View File

@@ -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() {

View 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() }
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `rb/polynomial-redos` query now considers the entrypoints of the API of a gem as sources.

View File

@@ -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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries
version: 0.5.3-dev
version: 0.5.4-dev
groups:
- ruby
- queries

View File

@@ -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()

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
source "https://rubygems.org"
gem "rack"
gem "webrick"
gem "twirp"

View File

@@ -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 |

View 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() }

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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" |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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)

View File

@@ -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