mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
QL: More cleanup
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
name: codeql-ruby-consistency-queries
|
||||
name: codeql-ql-consistency-queries
|
||||
version: 0.0.0
|
||||
libraryPathDependencies:
|
||||
- codeql-ruby
|
||||
extractor: ruby
|
||||
- codeql-ql
|
||||
extractor: ql
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
name: codeql-ruby-examples
|
||||
name: codeql-ql-examples
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql-ruby
|
||||
libraryPathDependencies: codeql-ql
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- description: Standard Code Scanning queries for Ruby
|
||||
- qlpack: codeql-ruby
|
||||
- description: Standard Code Scanning queries for QL
|
||||
- qlpack: codeql-ql
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- description: Standard LGTM queries for Ruby, including ones not displayed by default
|
||||
- qlpack: codeql-ruby
|
||||
- description: Standard LGTM queries for QL, including ones not displayed by default
|
||||
- qlpack: codeql-ql
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
# These are only for IDE use.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- description: Standard LGTM queries for Ruby
|
||||
- apply: codeql-suites/ruby-lgtm-full.qls
|
||||
- description: Standard LGTM queries for QL
|
||||
- apply: codeql-suites/ql-lgtm-full.qls
|
||||
- apply: lgtm-displayed-only.yml
|
||||
from: codeql-suite-helpers
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- description: Security-and-quality queries for Ruby
|
||||
- qlpack: codeql-ruby
|
||||
- description: Security-and-quality queries for QL
|
||||
- qlpack: codeql-ql
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- description: Security-extended queries for Ruby
|
||||
- qlpack: codeql-ruby
|
||||
- description: Security-extended queries for QL
|
||||
- qlpack: codeql-ql
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
|
||||
@@ -178,7 +178,9 @@ class File extends Container, @file {
|
||||
token = this.getAToken() and
|
||||
l = token.getLocation() and
|
||||
line in [l.getStartLine() .. l.getEndLine()] and
|
||||
if token instanceof @token_block_comment then comment = true else comment = false
|
||||
if token instanceof @token_block_comment or token instanceof @token_line_comment
|
||||
then comment = true
|
||||
else comment = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
52
ql/src/codeql_ql/Diagnostics.qll
Normal file
52
ql/src/codeql_ql/Diagnostics.qll
Normal file
@@ -0,0 +1,52 @@
|
||||
private import codeql.Locations
|
||||
|
||||
/** A diagnostic emitted during extraction, such as a parse error */
|
||||
class Diagnostic extends @diagnostic {
|
||||
int severity;
|
||||
string tag;
|
||||
string message;
|
||||
string fullMessage;
|
||||
Location location;
|
||||
|
||||
Diagnostic() { diagnostics(this, severity, tag, message, fullMessage, location) }
|
||||
|
||||
/**
|
||||
* Gets the numerical severity level associated with this diagnostic.
|
||||
*/
|
||||
int getSeverity() { result = severity }
|
||||
|
||||
/** Gets a string representation of the severity of this diagnostic. */
|
||||
string getSeverityText() {
|
||||
severity = 10 and result = "Debug"
|
||||
or
|
||||
severity = 20 and result = "Info"
|
||||
or
|
||||
severity = 30 and result = "Warning"
|
||||
or
|
||||
severity = 40 and result = "Error"
|
||||
}
|
||||
|
||||
/** Gets the error code associated with this diagnostic, e.g. parse_error. */
|
||||
string getTag() { result = tag }
|
||||
|
||||
/**
|
||||
* Gets the error message text associated with this diagnostic.
|
||||
*/
|
||||
string getMessage() { result = message }
|
||||
|
||||
/**
|
||||
* Gets the full error message text associated with this diagnostic.
|
||||
*/
|
||||
string getFullMessage() { result = fullMessage }
|
||||
|
||||
/** Gets the source location of this diagnostic. */
|
||||
Location getLocation() { result = location }
|
||||
|
||||
/** Gets a textual representation of this diagnostic. */
|
||||
string toString() { result = this.getMessage() }
|
||||
}
|
||||
|
||||
/** A diagnostic relating to a particular error in extracting a file. */
|
||||
class ExtractionError extends Diagnostic, @diagnostic_error {
|
||||
ExtractionError() { this.getTag() = "parse_error" }
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for jump-to-definition in the code viewer.
|
||||
* @kind definitions
|
||||
* @id ruby/ide-jump-to-definition
|
||||
* @id ql/ide-jump-to-definition
|
||||
* @tags ide-contextual-queries/local-definitions
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for find-references in the code viewer.
|
||||
* @kind definitions
|
||||
* @id ruby/ide-find-references
|
||||
* @id ql/ide-find-references
|
||||
* @tags ide-contextual-queries/local-references
|
||||
*/
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Print AST
|
||||
* @description Produces a representation of a file's Abstract Syntax Tree.
|
||||
* This query is used by the VS Code extension.
|
||||
* @id ruby/print-ast
|
||||
* @id ql/print-ast
|
||||
* @kind graph
|
||||
* @tags ide-contextual-queries/print-ast
|
||||
*/
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import codeql.files.FileSystem
|
||||
import codeql_ql.ast.internal.TreeSitter
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql-ruby
|
||||
name: codeql-ql
|
||||
version: 0.0.0
|
||||
dbscheme: ql.dbscheme
|
||||
suites: codeql-suites
|
||||
extractor: ruby
|
||||
extractor: ql
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
* @name Extraction errors
|
||||
* @description List all extraction errors for files in the source code directory.
|
||||
* @kind diagnostic
|
||||
* @id rb/diagnostics/extraction-errors
|
||||
* @id ql/diagnostics/extraction-errors
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
import codeql_ql.Diagnostics
|
||||
|
||||
/** Gets the SARIF severity to associate an error. */
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* @description Lists all files in the source code directory that were extracted
|
||||
* without encountering an error in the file.
|
||||
* @kind diagnostic
|
||||
* @id rb/diagnostics/successfully-extracted-files
|
||||
* @id ql/diagnostics/successfully-extracted-files
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
import codeql_ql.Diagnostics
|
||||
|
||||
from File f
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* @kind metric
|
||||
* @description The number of lines in each file.
|
||||
* @metricType file
|
||||
* @id rb/lines-per-file
|
||||
* @id ql/lines-per-file
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
|
||||
from File f, int n
|
||||
where n = f.getNumberOfLines()
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
* @description Measures the number of lines of code in each file, ignoring lines that
|
||||
* contain only comments or whitespace.
|
||||
* @metricType file
|
||||
* @id rb/lines-of-code-in-files
|
||||
* @id ql/lines-of-code-in-files
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
|
||||
from File f, int n
|
||||
where n = f.getNumberOfLinesOfCode()
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* @kind metric
|
||||
* @description Measures the number of lines of comments in each file.
|
||||
* @metricType file
|
||||
* @id rb/lines-of-comments-in-files
|
||||
* @id ql/lines-of-comments-in-files
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
|
||||
from File f, int n
|
||||
where n = f.getNumberOfLinesOfComments()
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* @name Use detect
|
||||
* @description Use 'detect' instead of 'select' followed by 'first' or 'last'.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id rb/use-detect
|
||||
* @tags performance rubocop
|
||||
* @precision high
|
||||
*
|
||||
* This is an implementation of Rubocop rule
|
||||
* https://github.com/rubocop/rubocop-performance/blob/master/lib/rubocop/cop/performance/detect.rb
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql_ql.dataflow.SSA
|
||||
|
||||
/** A call that extracts the first or last element of a list. */
|
||||
class EndCall extends MethodCall {
|
||||
string detect;
|
||||
|
||||
EndCall() {
|
||||
detect = "detect" and
|
||||
(
|
||||
this.getMethodName() = "first" and
|
||||
this.getNumberOfArguments() = 0
|
||||
or
|
||||
this.getNumberOfArguments() = 1 and
|
||||
this.getArgument(0).(IntegerLiteral).getValueText() = "0"
|
||||
)
|
||||
or
|
||||
detect = "reverse_detect" and
|
||||
(
|
||||
this.getMethodName() = "last" and
|
||||
this.getNumberOfArguments() = 0
|
||||
or
|
||||
this.getNumberOfArguments() = 1 and
|
||||
this.getArgument(0).(UnaryMinusExpr).getOperand().(IntegerLiteral).getValueText() = "1"
|
||||
)
|
||||
}
|
||||
|
||||
string detectCall() { result = detect }
|
||||
}
|
||||
|
||||
Expr getUniqueRead(Expr e) {
|
||||
exists(AssignExpr ae |
|
||||
e = ae.getRightOperand() and
|
||||
forex(Ssa::WriteDefinition def | def.getWriteAccess() = ae.getLeftOperand() |
|
||||
strictcount(def.getARead()) = 1 and
|
||||
not def = any(Ssa::PhiNode phi).getAnInput() and
|
||||
def.getARead() = result.getAControlFlowNode()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class SelectBlock extends MethodCall {
|
||||
SelectBlock() {
|
||||
this.getMethodName() in ["select", "filter", "find_all"] and
|
||||
exists(this.getBlock())
|
||||
}
|
||||
}
|
||||
|
||||
from EndCall call, SelectBlock selectBlock
|
||||
where getUniqueRead*(selectBlock) = call.getReceiver()
|
||||
select call, "Replace this call and $@ with '" + call.detectCall() + "'.", selectBlock,
|
||||
"'select' call"
|
||||
@@ -1,26 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
When creating a file, POSIX systems allow permissions to be specified
|
||||
for owner, group and others separately. Permissions should be kept as
|
||||
strict as possible, preventing access to the files contents by other users.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Restrict the file permissions of files to prevent any but the owner being able to read or write to that file
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Wikipedia:
|
||||
<a href="https://en.wikipedia.org/wiki/File_system_permissions">File system permissions</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* @name Overly permissive file permissions
|
||||
* @description Allowing files to be readable or writable by users other than the owner may allow sensitive information to be accessed.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id rb/overly-permissive-file
|
||||
* @tags external/cwe/cwe-732
|
||||
* security
|
||||
* @precision low
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql_ql.DataFlow
|
||||
import DataFlow::PathGraph
|
||||
private import codeql_ql.dataflow.SSA
|
||||
|
||||
// TODO: account for flows through tuple assignments
|
||||
/** An expression referencing the File or FileUtils module */
|
||||
class FileModuleAccess extends Expr {
|
||||
FileModuleAccess() {
|
||||
this.(ConstantAccess).getName() = "File"
|
||||
or
|
||||
this.(ConstantAccess).getName() = "FileUtils"
|
||||
or
|
||||
exists(FileModuleAccess fma, Ssa::WriteDefinition def |
|
||||
def.getARead() = this.getAControlFlowNode() and
|
||||
def.getWriteAccess().getParent().(Assignment).getRightOperand() = fma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[p]
|
||||
int world_permission(int p) { result = p.bitAnd(7) }
|
||||
|
||||
// 70 oct = 56 dec
|
||||
bindingset[p]
|
||||
int group_permission(int p) { result = p.bitAnd(56) }
|
||||
|
||||
bindingset[p]
|
||||
string access(int p) {
|
||||
p.bitAnd(2) != 0 and result = "writable"
|
||||
or
|
||||
p.bitAnd(4) != 0 and result = "readable"
|
||||
}
|
||||
|
||||
/** An expression specifing a file permission that allows group/others read or write access */
|
||||
class PermissivePermissionsExpr extends Expr {
|
||||
// TODO: non-literal expressions?
|
||||
PermissivePermissionsExpr() {
|
||||
exists(int perm, string acc |
|
||||
perm = this.(IntegerLiteral).getValue() and
|
||||
(acc = access(world_permission(perm)) or acc = access(group_permission(perm)))
|
||||
)
|
||||
or
|
||||
// adding/setting read or write permissions for all/group/other
|
||||
this.(StringLiteral).getValueText().regexpMatch(".*[ago][^-=+]*[+=][xXst]*[rw].*")
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a method of File or FileUtils that may modify file permissions */
|
||||
class PermissionSettingMethodCall extends MethodCall {
|
||||
private string methodName;
|
||||
private Expr permArg;
|
||||
|
||||
PermissionSettingMethodCall() {
|
||||
this.getReceiver() instanceof FileModuleAccess and
|
||||
this.getMethodName() = methodName and
|
||||
(
|
||||
methodName in ["chmod", "chmod_R", "lchmod"] and permArg = this.getArgument(0)
|
||||
or
|
||||
methodName = "mkfifo" and permArg = this.getArgument(1)
|
||||
or
|
||||
methodName in ["new", "open"] and permArg = this.getArgument(2)
|
||||
or
|
||||
methodName in ["install", "makedirs", "mkdir", "mkdir_p", "mkpath"] and
|
||||
permArg = this.getKeywordArgument("mode")
|
||||
// TODO: defaults for optional args? This may depend on the umask
|
||||
)
|
||||
}
|
||||
|
||||
Expr getPermissionArgument() { result = permArg }
|
||||
}
|
||||
|
||||
class PermissivePermissionsConfig extends DataFlow::Configuration {
|
||||
PermissivePermissionsConfig() { this = "PermissivePermissionsConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(PermissivePermissionsExpr ppe | source.asExpr().getExpr() = ppe)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(PermissionSettingMethodCall c | sink.asExpr().getExpr() = c.getPermissionArgument())
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, PermissivePermissionsConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Overly permissive mask sets file to $@.", source.getNode(),
|
||||
source.getNode().toString()
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @id rb/summary/lines-of-code
|
||||
* @name Total lines of Ruby code in the database
|
||||
* @description The total number of lines of Ruby code from the source code
|
||||
* @id ql/summary/lines-of-code
|
||||
* @name Total lines of QL code in the database
|
||||
* @description The total number of lines of QL code from the source code
|
||||
* directory, including external libraries and auto-generated files. This is a
|
||||
* useful metric of the size of a database. This query counts the lines of
|
||||
* code, excluding whitespace or comments.
|
||||
@@ -10,6 +10,6 @@
|
||||
* lines-of-code
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
|
||||
select sum(File f | exists(f.getRelativePath()) | f.getNumberOfLinesOfCode())
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/**
|
||||
* @id rb/summary/lines-of-user-code
|
||||
* @name Total Lines of user written Ruby code in the database
|
||||
* @description The total number of lines of Ruby code from the source code
|
||||
* @id ql/summary/lines-of-user-code
|
||||
* @name Total Lines of user written QL code in the database
|
||||
* @description The total number of lines of QL code from the source code
|
||||
* directory, excluding external library and auto-generated files. This
|
||||
* query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
|
||||
select sum(File f |
|
||||
f.fromSource() and
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* @id rb/summary/number-of-files-extracted-with-errors
|
||||
* @id ql/summary/number-of-files-extracted-with-errors
|
||||
* @name Total number of files that were extracted with errors
|
||||
* @description The total number of Ruby code files that we extracted, but where
|
||||
* @description The total number of QL code files that we extracted, but where
|
||||
* at least one extraction error occurred in the process.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
import codeql_ql.Diagnostics
|
||||
|
||||
select count(File f |
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* @id rb/summary/number-of-successfully-extracted-files
|
||||
* @id ql/summary/number-of-successfully-extracted-files
|
||||
* @name Total number of files that were extracted without error
|
||||
* @description The total number of Ruby code files that we extracted without
|
||||
* @description The total number of QL code files that we extracted without
|
||||
* encountering any extraction errors
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import ql
|
||||
import codeql_ql.Diagnostics
|
||||
|
||||
select count(File f |
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* @name Useless assignment to local variable
|
||||
* @description An assignment to a local variable that is not used later on, or whose value is always
|
||||
* overwritten, has no effect.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id rb/useless-assignment-to-local
|
||||
* @tags maintainability
|
||||
* external/cwe/cwe-563
|
||||
* @precision low
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql_ql.dataflow.SSA
|
||||
|
||||
class RelevantLocalVariableWriteAccess extends LocalVariableWriteAccess {
|
||||
RelevantLocalVariableWriteAccess() {
|
||||
not this.getVariable().getName().charAt(0) = "_" and
|
||||
not this = any(Parameter p).getAVariable().getDefiningAccess()
|
||||
}
|
||||
}
|
||||
|
||||
from RelevantLocalVariableWriteAccess write, LocalVariable v
|
||||
where
|
||||
v = write.getVariable() and
|
||||
exists(write.getAControlFlowNode()) and
|
||||
not exists(Ssa::WriteDefinition def | def.getWriteAccess() = write)
|
||||
select write, "This assignment to $@ is useless, since its value is never read.", v, v.getName()
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* @name Potentially uninitialized local variable
|
||||
* @description Using a local variable before it is initialized gives the variable a default
|
||||
* 'nil' value.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id rb/uninitialized-local-variable
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* @precision low
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql_ql.dataflow.SSA
|
||||
|
||||
class RelevantLocalVariableReadAccess extends LocalVariableReadAccess {
|
||||
RelevantLocalVariableReadAccess() {
|
||||
not exists(MethodCall c |
|
||||
c.getReceiver() = this and
|
||||
c.getMethodName() = "nil?"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from RelevantLocalVariableReadAccess read, LocalVariable v
|
||||
where
|
||||
v = read.getVariable() and
|
||||
exists(Ssa::Definition def |
|
||||
def.getAnUltimateDefinition() instanceof Ssa::UninitializedDefinition and
|
||||
read = def.getARead().getExpr()
|
||||
)
|
||||
select read, "Local variable $@ may be used before it is initialized.", v, v.getName()
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @name Unused parameter.
|
||||
* @description A parameter that is not used later on, or whose value is always overwritten,
|
||||
* can be removed.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id rb/unused-parameter
|
||||
* @tags maintainability
|
||||
* external/cwe/cwe-563
|
||||
* @precision low
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql_ql.dataflow.SSA
|
||||
|
||||
class RelevantParameterVariable extends LocalVariable {
|
||||
RelevantParameterVariable() {
|
||||
exists(Parameter p |
|
||||
this = p.getAVariable() and
|
||||
not this.getName().charAt(0) = "_"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from RelevantParameterVariable v
|
||||
where not exists(Ssa::WriteDefinition def | def.getWriteAccess() = v.getDefiningAccess())
|
||||
select v, "Unused parameter."
|
||||
@@ -1,7 +1,7 @@
|
||||
name: codeql-ruby-tests
|
||||
name: codeql-ql-tests
|
||||
version: 0.0.0
|
||||
libraryPathDependencies:
|
||||
- codeql-ruby
|
||||
- codeql-ruby-examples
|
||||
extractor: ruby
|
||||
- codeql-ql
|
||||
- codeql-ql-examples
|
||||
extractor: ql
|
||||
tests: .
|
||||
|
||||
Reference in New Issue
Block a user