mirror of
https://github.com/github/codeql.git
synced 2026-04-22 23:35:14 +02:00
Merge branch 'main' into java/update-mad-decls-after-triage-2024-01-24T10-05-04
This commit is contained in:
@@ -473,10 +473,6 @@
|
||||
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
|
||||
],
|
||||
"Typo database": [
|
||||
"javascript/ql/src/Expressions/TypoDatabase.qll",
|
||||
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
|
||||
],
|
||||
"Swift declarations test file": [
|
||||
"swift/ql/test/extractor-tests/declarations/declarations.swift",
|
||||
"swift/ql/test/library-tests/ast/declarations.swift"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted many deprecated predicates and classes with uppercase `XML`, `SSA`, `SAL`, `SQL`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `StrcatFunction` class, use `semmle.code.cpp.models.implementations.Strcat.qll` instead.
|
||||
@@ -380,9 +380,6 @@ class Class extends UserType {
|
||||
*/
|
||||
predicate isPod() { is_pod_class(underlyingElement(this)) }
|
||||
|
||||
/** DEPRECATED: Alias for isPod */
|
||||
deprecated predicate isPOD() { this.isPod() }
|
||||
|
||||
/**
|
||||
* Holds if this class, struct or union is a standard-layout class
|
||||
* [N4140 9(7)]. Also holds for structs in C programs.
|
||||
|
||||
@@ -104,9 +104,6 @@ predicate isPodClass03(Class c) {
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isPodClass03 */
|
||||
deprecated predicate isPODClass03 = isPodClass03/1;
|
||||
|
||||
/**
|
||||
* Holds if `t` is a POD type, according to the rules specified in
|
||||
* C++03 3.9(10):
|
||||
@@ -126,6 +123,3 @@ predicate isPodType03(Type t) {
|
||||
isPodType03(ut.(SpecifiedType).getUnspecifiedType())
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isPodType03 */
|
||||
deprecated predicate isPODType03 = isPodType03/1;
|
||||
|
||||
@@ -32,9 +32,6 @@ class XmlLocatable extends @xmllocatable, TXmlLocatable {
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlLocatable */
|
||||
deprecated class XMLLocatable = XmlLocatable;
|
||||
|
||||
/**
|
||||
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
|
||||
* both of which can contain other elements.
|
||||
@@ -95,9 +92,6 @@ class XmlParent extends @xmlparent {
|
||||
string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlParent */
|
||||
deprecated class XMLParent = XmlParent;
|
||||
|
||||
/** An XML file. */
|
||||
class XmlFile extends XmlParent, File {
|
||||
XmlFile() { xmlEncoding(this, _) }
|
||||
@@ -119,14 +113,8 @@ class XmlFile extends XmlParent, File {
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
|
||||
|
||||
/** DEPRECATED: Alias for getADtd */
|
||||
deprecated XmlDtd getADTD() { result = this.getADtd() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlFile */
|
||||
deprecated class XMLFile = XmlFile;
|
||||
|
||||
/**
|
||||
* An XML document type definition (DTD).
|
||||
*
|
||||
@@ -163,9 +151,6 @@ class XmlDtd extends XmlLocatable, @xmldtd {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlDtd */
|
||||
deprecated class XMLDTD = XmlDtd;
|
||||
|
||||
/**
|
||||
* An XML element in an XML file.
|
||||
*
|
||||
@@ -221,9 +206,6 @@ class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
|
||||
override string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlElement */
|
||||
deprecated class XMLElement = XmlElement;
|
||||
|
||||
/**
|
||||
* An attribute that occurs inside an XML element.
|
||||
*
|
||||
@@ -254,9 +236,6 @@ class XmlAttribute extends @xmlattribute, XmlLocatable {
|
||||
override string toString() { result = this.getName() + "=" + this.getValue() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlAttribute */
|
||||
deprecated class XMLAttribute = XmlAttribute;
|
||||
|
||||
/**
|
||||
* A namespace used in an XML file.
|
||||
*
|
||||
@@ -273,9 +252,6 @@ class XmlNamespace extends XmlLocatable, @xmlnamespace {
|
||||
/** Gets the URI of this namespace. */
|
||||
string getUri() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** DEPRECATED: Alias for getUri */
|
||||
deprecated string getURI() { result = this.getUri() }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
@@ -286,9 +262,6 @@ class XmlNamespace extends XmlLocatable, @xmlnamespace {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlNamespace */
|
||||
deprecated class XMLNamespace = XmlNamespace;
|
||||
|
||||
/**
|
||||
* A comment in an XML file.
|
||||
*
|
||||
@@ -309,9 +282,6 @@ class XmlComment extends @xmlcomment, XmlLocatable {
|
||||
override string toString() { result = this.getText() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlComment */
|
||||
deprecated class XMLComment = XmlComment;
|
||||
|
||||
/**
|
||||
* A sequence of characters that occurs between opening and
|
||||
* closing tags of an XML element, excluding other elements.
|
||||
@@ -335,6 +305,3 @@ class XmlCharacters extends @xmlcharacters, XmlLocatable {
|
||||
/** Gets a printable representation of this XML character sequence. */
|
||||
override string toString() { result = this.getCharacters() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlCharacters */
|
||||
deprecated class XMLCharacters = XmlCharacters;
|
||||
|
||||
@@ -5,9 +5,6 @@ class NullMacro extends Macro {
|
||||
NullMacro() { this.getHead() = "NULL" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NullMacro */
|
||||
deprecated class NULLMacro = NullMacro;
|
||||
|
||||
/** A use of the NULL macro. */
|
||||
class NULL extends Literal {
|
||||
NULL() { exists(NullMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) }
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `semmle.code.cpp.models.implementations.Strcat.qll` instead.
|
||||
*
|
||||
* A function that concatenates the string from its second argument
|
||||
* to the string from its first argument, for example `strcat`.
|
||||
*/
|
||||
deprecated class StrcatFunction extends Function {
|
||||
StrcatFunction() {
|
||||
this.getName() =
|
||||
[
|
||||
"strcat", // strcat(dst, src)
|
||||
"strncat", // strncat(dst, src, max_amount)
|
||||
"wcscat", // wcscat(dst, src)
|
||||
"_mbscat", // _mbscat(dst, src)
|
||||
"wcsncat", // wcsncat(dst, src, max_amount)
|
||||
"_mbsncat", // _mbsncat(dst, src, max_amount)
|
||||
"_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1394,11 +1394,21 @@ abstract private class ExprNodeBase extends Node {
|
||||
final Expr getExpr(int n) { result = this.getConvertedExpr(n).getUnconverted() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a dataflow node whose `asExpr(n)` should evaluate
|
||||
* to `e`.
|
||||
*/
|
||||
private predicate exprNodeShouldBe(Expr e, int n) {
|
||||
exprNodeShouldBeInstruction(_, e, n) or
|
||||
exprNodeShouldBeOperand(_, e, n) or
|
||||
exprNodeShouldBeIndirectOutNode(_, e, n)
|
||||
}
|
||||
|
||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||
InstructionExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeInstruction(this, e, n) and
|
||||
not exprNodeShouldBeInstruction(_, e, n + 1)
|
||||
not exprNodeShouldBe(e, n + 1)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1409,7 +1419,7 @@ private class OperandExprNode extends ExprNodeBase, OperandNode {
|
||||
OperandExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeOperand(this, e, n) and
|
||||
not exprNodeShouldBeOperand(_, e, n + 1)
|
||||
not exprNodeShouldBe(e, n + 1)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -609,7 +609,10 @@ class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
|
||||
*/
|
||||
predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
|
||||
exists(IRBlock bb1, int i1, SourceVariable v |
|
||||
defOrUse1.asDefOrUse().hasIndexInBlock(bb1, i1, v)
|
||||
defOrUse1
|
||||
.asDefOrUse()
|
||||
.hasIndexInBlock(pragma[only_bind_out](bb1), pragma[only_bind_out](i1),
|
||||
pragma[only_bind_out](v))
|
||||
|
|
||||
exists(IRBlock bb2, int i2, DefinitionExt def |
|
||||
adjacentDefReadExt(pragma[only_bind_into](def), pragma[only_bind_into](bb1),
|
||||
@@ -631,7 +634,11 @@ predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
|
||||
* flows to `useOrPhi`.
|
||||
*/
|
||||
private predicate globalDefToUse(GlobalDef globalDef, UseOrPhi useOrPhi) {
|
||||
exists(IRBlock bb1, int i1, SourceVariable v | globalDef.hasIndexInBlock(bb1, i1, v) |
|
||||
exists(IRBlock bb1, int i1, SourceVariable v |
|
||||
globalDef
|
||||
.hasIndexInBlock(pragma[only_bind_out](bb1), pragma[only_bind_out](i1),
|
||||
pragma[only_bind_out](v))
|
||||
|
|
||||
exists(IRBlock bb2, int i2 |
|
||||
adjacentDefReadExt(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
|
||||
pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
|
||||
|
||||
@@ -1068,6 +1068,3 @@ module Ssa {
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Ssa */
|
||||
deprecated module SSA = Ssa;
|
||||
|
||||
@@ -3,13 +3,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Rea
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSsaInstructions as SsaInstructions
|
||||
|
||||
/** DEPRECATED: Alias for SsaInstructions */
|
||||
deprecated module SSAInstructions = SsaInstructions;
|
||||
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand::AliasedSsaOperands as SsaOperands
|
||||
|
||||
/** DEPRECATED: Alias for SsaOperands */
|
||||
deprecated module SSAOperands = SsaOperands;
|
||||
|
||||
@@ -2,6 +2,3 @@ import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSsa
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSsa
|
||||
|
||||
/** DEPRECATED: Alias for AliasedSsa */
|
||||
deprecated module AliasedSSA = AliasedSsa;
|
||||
|
||||
@@ -1068,6 +1068,3 @@ module Ssa {
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Ssa */
|
||||
deprecated module SSA = Ssa;
|
||||
|
||||
@@ -4,13 +4,6 @@ import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSsaInstructions as SsaInstructions
|
||||
|
||||
/** DEPRECATED: Alias for SsaInstructions */
|
||||
deprecated module SSAInstructions = SsaInstructions;
|
||||
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand::UnaliasedSsaOperands as SsaOperands
|
||||
|
||||
/** DEPRECATED: Alias for SsaOperands */
|
||||
deprecated module SSAOperands = SsaOperands;
|
||||
|
||||
@@ -27,10 +27,12 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
or
|
||||
this.hasGlobalOrStdName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"ExFreePoolWithTag", "ExDeleteTimer", "IoFreeMdl", "IoFreeWorkItem", "IoFreeErrorLogEntry",
|
||||
"MmFreeContiguousMemory", "MmFreeContiguousMemorySpecifyCache", "MmFreeNonCachedMemory",
|
||||
"MmFreeMappingAddress", "MmFreePagesFromMdl", "MmUnmapReservedMapping",
|
||||
"MmUnmapLockedPages",
|
||||
"ExFreePool", "ExFreePoolWithTag", "ExDeleteTimer", "IoFreeIrp", "IoFreeMdl",
|
||||
"IoFreeErrorLogEntry", "IoFreeWorkItem", "MmFreeContiguousMemory",
|
||||
"MmFreeContiguousMemorySpecifyCache", "MmFreeNonCachedMemory", "MmFreeMappingAddress",
|
||||
"MmFreePagesFromMdl", "MmUnmapReservedMapping", "MmUnmapLockedPages",
|
||||
"NdisFreeGenericObject", "NdisFreeMemory", "NdisFreeMemoryWithTag", "NdisFreeMdl",
|
||||
"NdisFreeNetBufferListPool", "NdisFreeNetBufferPool",
|
||||
// --- Windows Global / Local legacy allocation
|
||||
"LocalFree", "GlobalFree", "LocalReAlloc", "GlobalReAlloc",
|
||||
// --- Windows System Services allocation
|
||||
@@ -47,6 +49,7 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
this.hasGlobalOrStdName([
|
||||
// --- Windows Memory Management for Windows Drivers
|
||||
"ExFreeToLookasideListEx", "ExFreeToPagedLookasideList", "ExFreeToNPagedLookasideList",
|
||||
"NdisFreeMemoryWithTagPriority", "StorPortFreeMdl", "StorPortFreePool",
|
||||
// --- NetBSD pool manager
|
||||
"pool_put", "pool_cache_put"
|
||||
]) and
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
/**
|
||||
* General library for finding flow from a pointer being freed to a user-specified sink
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Signature for a predicate that holds if `n.asExpr() = e` and `n` is a sink in
|
||||
* the `FlowFromFreeConfig` module.
|
||||
*/
|
||||
private signature predicate isSinkSig(DataFlow::Node n, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if `dealloc` is a deallocation expression and `e` is an expression such
|
||||
* that `isFree(_, e)` holds for some `isFree` predicate satisfying `isSinkSig`,
|
||||
* and this source-sink pair should be excluded from the analysis.
|
||||
*/
|
||||
bindingset[dealloc, e]
|
||||
private signature predicate isExcludedSig(DeallocationExpr dealloc, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if `(b1, i1)` strictly post-dominates `(b2, i2)`
|
||||
*/
|
||||
@@ -38,6 +28,31 @@ predicate strictlyDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
|
||||
b1.strictlyDominates(b2)
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature for a module that is used to specify the inputs to the `FlowFromFree` module.
|
||||
*/
|
||||
signature module FlowFromFreeParamSig {
|
||||
/**
|
||||
* Holds if `n.asExpr() = e` and `n` is a sink in the `FlowFromFreeConfig`
|
||||
* module.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node n, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if `dealloc` is a deallocation expression and `e` is an expression such
|
||||
* that `isFree(_, e)` holds for some `isFree` predicate satisfying `isSinkSig`,
|
||||
* and this source-sink pair should be excluded from the analysis.
|
||||
*/
|
||||
bindingset[dealloc, e]
|
||||
predicate isExcluded(DeallocationExpr dealloc, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if `sink` should be considered a `sink` when the source of flow is `source`.
|
||||
*/
|
||||
bindingset[source, sink]
|
||||
default predicate sourceSinkIsRelated(DataFlow::Node source, DataFlow::Node sink) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `FlowFromFreeConfig` module that can be used to find flow between
|
||||
* a pointer being freed by some deallocation function, and a user-specified sink.
|
||||
@@ -47,8 +62,8 @@ predicate strictlyDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
|
||||
* 1. The source dominates the sink, or
|
||||
* 2. The sink post-dominates the source.
|
||||
*/
|
||||
module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
||||
module FlowFromFreeConfig implements DataFlow::StateConfigSig {
|
||||
module FlowFromFree<FlowFromFreeParamSig P> {
|
||||
private module FlowFromFreeConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState instanceof Expr {
|
||||
FlowState() { isFree(_, _, this, _) }
|
||||
|
||||
@@ -59,20 +74,12 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
||||
|
||||
pragma[inline]
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(
|
||||
Expr e, DataFlow::Node source, IRBlock b1, int i1, IRBlock b2, int i2,
|
||||
DeallocationExpr dealloc
|
||||
|
|
||||
isASink(sink, e) and
|
||||
exists(Expr e, DataFlow::Node source, DeallocationExpr dealloc |
|
||||
P::isSink(sink, e) and
|
||||
isFree(source, _, state, dealloc) and
|
||||
e != state and
|
||||
source.hasIndexInBlock(b1, i1) and
|
||||
sink.hasIndexInBlock(b2, i2) and
|
||||
not isExcluded(dealloc, e)
|
||||
|
|
||||
strictlyDominates(b1, i1, b2, i2)
|
||||
or
|
||||
strictlyPostDominates(b2, i2, b1, i1)
|
||||
not P::isExcluded(dealloc, e) and
|
||||
P::sourceSinkIsRelated(source, sink)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -127,3 +134,38 @@ predicate isExFreePoolCall(FunctionCall fc, Expr e) {
|
||||
fc.getTarget().hasGlobalName("ExFreePool")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if either `source` strictly dominates `sink`, or `sink` strictly
|
||||
* post-dominates `source`.
|
||||
*/
|
||||
bindingset[source, sink]
|
||||
predicate defaultSourceSinkIsRelated(DataFlow::Node source, DataFlow::Node sink) {
|
||||
exists(IRBlock b1, int i1, IRBlock b2, int i2 |
|
||||
source.hasIndexInBlock(b1, i1) and
|
||||
sink.hasIndexInBlock(b2, i2)
|
||||
|
|
||||
strictlyDominates(b1, i1, b2, i2)
|
||||
or
|
||||
strictlyPostDominates(b2, i2, b1, i1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `dealloc1` is a deallocation expression, `e` is an expression that dereferences a
|
||||
* pointer, and the `(dealloc1, e)` pair should be excluded by the `FlowFromFree` library.
|
||||
*
|
||||
* Note that `e` is not necessarily the expression deallocated by `dealloc1`. It will
|
||||
* be bound to the second deallocation as identified by the `FlowFromFree` library.
|
||||
*
|
||||
* From https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmfreepagesfrommdl:
|
||||
* "After calling MmFreePagesFromMdl, the caller must also call ExFreePool
|
||||
* to release the memory that was allocated for the MDL structure."
|
||||
*/
|
||||
bindingset[dealloc1, e]
|
||||
predicate isExcludedMmFreePageFromMdl(DeallocationExpr dealloc1, Expr e) {
|
||||
exists(DeallocationExpr dealloc2 | isFree(_, _, e, dealloc2) |
|
||||
dealloc1.(FunctionCall).getTarget().hasGlobalName("MmFreePagesFromMdl") and
|
||||
isExFreePoolCall(dealloc2, _)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* General library for tracing Use After Free vulnerabilities.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.security.flowafterfree.FlowAfterFree
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Holds if `call` is a call to a function that obviously
|
||||
* doesn't dereference its `i`'th argument.
|
||||
*/
|
||||
private predicate externalCallNeverDereferences(FormattingFunctionCall call, int arg) {
|
||||
exists(int formatArg |
|
||||
pragma[only_bind_out](call.getFormatArgument(formatArg)) =
|
||||
pragma[only_bind_out](call.getArgument(arg)) and
|
||||
call.getFormat().(FormatLiteral).getConvSpec(formatArg) != "%s"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is a use. A use is a pointer dereference or a
|
||||
* parameter to a call with no function definition.
|
||||
* Uses in deallocation expressions (e.g., free) are excluded.
|
||||
* Default isUse definition for an expression.
|
||||
*/
|
||||
predicate isUse0(Expr e) {
|
||||
not isFree(_, _, e, _) and
|
||||
(
|
||||
// TODO: use DirectDefereferencedByOperation in Dereferenced.qll
|
||||
e = any(PointerDereferenceExpr pde).getOperand()
|
||||
or
|
||||
e = any(PointerFieldAccess pfa).getQualifier()
|
||||
or
|
||||
e = any(ArrayExpr ae).getArrayBase()
|
||||
or
|
||||
e = any(Call call).getQualifier()
|
||||
or
|
||||
// Assume any function without a body will dereference the pointer
|
||||
exists(int i, Call call, Function f |
|
||||
e = call.getArgument(i) and
|
||||
f = call.getTarget() and
|
||||
not f.hasEntryPoint() and
|
||||
// Exclude known functions we know won't dereference the pointer.
|
||||
// For example, a call such as `printf("%p", myPointer)`.
|
||||
not externalCallNeverDereferences(call, i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private module ParameterSinks {
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
private predicate flowsToUse(DataFlow::Node n) {
|
||||
isUse0(n.asExpr())
|
||||
or
|
||||
exists(DataFlow::Node succ |
|
||||
flowsToUse(succ) and
|
||||
DataFlow::localFlowStep(n, succ)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowsFromParam(DataFlow::Node n) {
|
||||
flowsToUse(n) and
|
||||
(
|
||||
n.asParameter().getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
exists(DataFlow::Node prev |
|
||||
flowsFromParam(prev) and
|
||||
DataFlow::localFlowStep(prev, n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
flowsFromParam(n1) and
|
||||
flowsFromParam(n2) and
|
||||
DataFlow::localFlowStep(n1, n2)
|
||||
}
|
||||
|
||||
private predicate paramToUse(DataFlow::Node n1, DataFlow::Node n2) = fastTC(step/2)(n1, n2)
|
||||
|
||||
private predicate hasFlow(
|
||||
DataFlow::Node source, InitializeParameterInstruction init, DataFlow::Node sink
|
||||
) {
|
||||
pragma[only_bind_out](source.asParameter()) = pragma[only_bind_out](init.getParameter()) and
|
||||
paramToUse(source, sink) and
|
||||
isUse0(sink.asExpr())
|
||||
}
|
||||
|
||||
private InitializeParameterInstruction getAnAlwaysDereferencedParameter0() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, IRBlock b1, int i1, IRBlock b2, int i2 |
|
||||
hasFlow(pragma[only_bind_into](source), result, pragma[only_bind_into](sink)) and
|
||||
source.hasIndexInBlock(b1, pragma[only_bind_into](i1)) and
|
||||
sink.hasIndexInBlock(b2, pragma[only_bind_into](i2)) and
|
||||
strictlyPostDominates(b2, i2, b1, i1)
|
||||
)
|
||||
}
|
||||
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction() {
|
||||
exists(IRFunction f | result.getBlock().postDominates(f.getEntryBlock()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callHasTargetAndArgument(Function f, int i, Instruction argument) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = f and
|
||||
call.getArgument(i) = argument and
|
||||
call = getAnAlwaysReachedCallInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initializeParameterInFunction(Function f, int i) {
|
||||
exists(InitializeParameterInstruction init |
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) = f and
|
||||
init.hasIndex(i) and
|
||||
init = getAnAlwaysDereferencedParameter()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate alwaysDereferencedArgumentHasValueNumber(ValueNumber vn) {
|
||||
exists(int i, Function f, Instruction argument |
|
||||
callHasTargetAndArgument(f, i, argument) and
|
||||
initializeParameterInFunction(pragma[only_bind_into](f), pragma[only_bind_into](i)) and
|
||||
vn.getAnInstruction() = argument
|
||||
)
|
||||
}
|
||||
|
||||
InitializeParameterInstruction getAnAlwaysDereferencedParameter() {
|
||||
result = getAnAlwaysDereferencedParameter0()
|
||||
or
|
||||
exists(ValueNumber vn |
|
||||
alwaysDereferencedArgumentHasValueNumber(vn) and
|
||||
vn.getAnInstruction() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* Holds if `n` represents the expression `e`, and `e` is a pointer that is
|
||||
* guaranteed to be dereferenced (either because it's an operand of a
|
||||
* dereference operation, or because it's an argument to a function that
|
||||
* always dereferences the parameter).
|
||||
*/
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(e) and n.asExpr() = e
|
||||
or
|
||||
exists(CallInstruction call, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
|
||||
viableParamArg(call, DataFlow::instructionNode(init), n) and
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) =
|
||||
pragma[only_bind_out](call.getStaticCallTarget())
|
||||
)
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import FlowAfterFree
|
||||
import semmle.code.cpp.security.flowafterfree.FlowAfterFree
|
||||
import DoubleFree::PathGraph
|
||||
|
||||
/**
|
||||
@@ -22,26 +22,15 @@ import DoubleFree::PathGraph
|
||||
*/
|
||||
predicate isFree(DataFlow::Node n, Expr e) { isFree(_, n, e, _) }
|
||||
|
||||
/**
|
||||
* `dealloc1` is a deallocation expression and `e` is an expression such
|
||||
* that is deallocated by a deallocation expression, and the `(dealloc1, e)` pair
|
||||
* should be excluded by the `FlowFromFree` library.
|
||||
*
|
||||
* Note that `e` is not necessarily the expression deallocated by `dealloc1`. It will
|
||||
* be bound to the second deallocation as identified by the `FlowFromFree` library.
|
||||
*/
|
||||
bindingset[dealloc1, e]
|
||||
predicate isExcludeFreePair(DeallocationExpr dealloc1, Expr e) {
|
||||
exists(DeallocationExpr dealloc2 | isFree(_, _, e, dealloc2) |
|
||||
dealloc1.(FunctionCall).getTarget().hasGlobalName("MmFreePagesFromMdl") and
|
||||
// From https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmfreepagesfrommdl:
|
||||
// "After calling MmFreePagesFromMdl, the caller must also call ExFreePool
|
||||
// to release the memory that was allocated for the MDL structure."
|
||||
isExFreePoolCall(dealloc2, _)
|
||||
)
|
||||
module DoubleFreeParam implements FlowFromFreeParamSig {
|
||||
predicate isSink = isFree/2;
|
||||
|
||||
predicate isExcluded = isExcludedMmFreePageFromMdl/2;
|
||||
|
||||
predicate sourceSinkIsRelated = defaultSourceSinkIsRelated/2;
|
||||
}
|
||||
|
||||
module DoubleFree = FlowFromFree<isFree/2, isExcludeFreePair/2>;
|
||||
module DoubleFree = FlowFromFree<DoubleFreeParam>;
|
||||
|
||||
from DoubleFree::PathNode source, DoubleFree::PathNode sink, DeallocationExpr dealloc, Expr e2
|
||||
where
|
||||
|
||||
@@ -14,170 +14,25 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.ir.IR
|
||||
import FlowAfterFree
|
||||
import UseAfterFree::PathGraph
|
||||
import semmle.code.cpp.security.flowafterfree.FlowAfterFree
|
||||
import semmle.code.cpp.security.flowafterfree.UseAfterFree
|
||||
import UseAfterFreeTrace::PathGraph
|
||||
|
||||
/**
|
||||
* Holds if `call` is a call to a function that obviously
|
||||
* doesn't dereference its `i`'th argument.
|
||||
*/
|
||||
private predicate externalCallNeverDereferences(FormattingFunctionCall call, int arg) {
|
||||
exists(int formatArg |
|
||||
pragma[only_bind_out](call.getFormatArgument(formatArg)) =
|
||||
pragma[only_bind_out](call.getArgument(arg)) and
|
||||
call.getFormat().(FormatLiteral).getConvSpec(formatArg) != "%s"
|
||||
)
|
||||
module UseAfterFreeParam implements FlowFromFreeParamSig {
|
||||
predicate isSink = isUse/2;
|
||||
|
||||
predicate isExcluded = isExcludedMmFreePageFromMdl/2;
|
||||
|
||||
predicate sourceSinkIsRelated = defaultSourceSinkIsRelated/2;
|
||||
}
|
||||
|
||||
predicate isUse0(Expr e) {
|
||||
not isFree(_, _, e, _) and
|
||||
(
|
||||
e = any(PointerDereferenceExpr pde).getOperand()
|
||||
or
|
||||
e = any(PointerFieldAccess pfa).getQualifier()
|
||||
or
|
||||
e = any(ArrayExpr ae).getArrayBase()
|
||||
or
|
||||
e = any(Call call).getQualifier()
|
||||
or
|
||||
// Assume any function without a body will dereference the pointer
|
||||
exists(int i, Call call, Function f |
|
||||
e = call.getArgument(i) and
|
||||
f = call.getTarget() and
|
||||
not f.hasEntryPoint() and
|
||||
// Exclude known functions we know won't dereference the pointer.
|
||||
// For example, a call such as `printf("%p", myPointer)`.
|
||||
not externalCallNeverDereferences(call, i)
|
||||
)
|
||||
)
|
||||
}
|
||||
import UseAfterFreeParam
|
||||
|
||||
module ParameterSinks {
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
module UseAfterFreeTrace = FlowFromFree<UseAfterFreeParam>;
|
||||
|
||||
predicate flowsToUse(DataFlow::Node n) {
|
||||
isUse0(n.asExpr())
|
||||
or
|
||||
exists(DataFlow::Node succ |
|
||||
flowsToUse(succ) and
|
||||
DataFlow::localFlowStep(n, succ)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowsFromParam(DataFlow::Node n) {
|
||||
flowsToUse(n) and
|
||||
(
|
||||
n.asParameter().getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
exists(DataFlow::Node prev |
|
||||
flowsFromParam(prev) and
|
||||
DataFlow::localFlowStep(prev, n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
flowsFromParam(n1) and
|
||||
flowsFromParam(n2) and
|
||||
DataFlow::localFlowStep(n1, n2)
|
||||
}
|
||||
|
||||
private predicate paramToUse(DataFlow::Node n1, DataFlow::Node n2) = fastTC(step/2)(n1, n2)
|
||||
|
||||
private predicate hasFlow(
|
||||
DataFlow::Node source, InitializeParameterInstruction init, DataFlow::Node sink
|
||||
) {
|
||||
pragma[only_bind_out](source.asParameter()) = pragma[only_bind_out](init.getParameter()) and
|
||||
paramToUse(source, sink) and
|
||||
isUse0(sink.asExpr())
|
||||
}
|
||||
|
||||
private InitializeParameterInstruction getAnAlwaysDereferencedParameter0() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, IRBlock b1, int i1, IRBlock b2, int i2 |
|
||||
hasFlow(pragma[only_bind_into](source), result, pragma[only_bind_into](sink)) and
|
||||
source.hasIndexInBlock(b1, pragma[only_bind_into](i1)) and
|
||||
sink.hasIndexInBlock(b2, pragma[only_bind_into](i2)) and
|
||||
strictlyPostDominates(b2, i2, b1, i1)
|
||||
)
|
||||
}
|
||||
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction() {
|
||||
exists(IRFunction f | result.getBlock().postDominates(f.getEntryBlock()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callHasTargetAndArgument(Function f, int i, Instruction argument) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = f and
|
||||
call.getArgument(i) = argument and
|
||||
call = getAnAlwaysReachedCallInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initializeParameterInFunction(Function f, int i) {
|
||||
exists(InitializeParameterInstruction init |
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) = f and
|
||||
init.hasIndex(i) and
|
||||
init = getAnAlwaysDereferencedParameter()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate alwaysDereferencedArgumentHasValueNumber(ValueNumber vn) {
|
||||
exists(int i, Function f, Instruction argument |
|
||||
callHasTargetAndArgument(f, i, argument) and
|
||||
initializeParameterInFunction(pragma[only_bind_into](f), pragma[only_bind_into](i)) and
|
||||
vn.getAnInstruction() = argument
|
||||
)
|
||||
}
|
||||
|
||||
InitializeParameterInstruction getAnAlwaysDereferencedParameter() {
|
||||
result = getAnAlwaysDereferencedParameter0()
|
||||
or
|
||||
exists(ValueNumber vn |
|
||||
alwaysDereferencedArgumentHasValueNumber(vn) and
|
||||
vn.getAnInstruction() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module IsUse {
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(e) and n.asExpr() = e
|
||||
or
|
||||
exists(CallInstruction call, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
|
||||
viableParamArg(call, DataFlow::instructionNode(init), n) and
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) =
|
||||
pragma[only_bind_out](call.getStaticCallTarget())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import IsUse
|
||||
|
||||
/**
|
||||
* `dealloc1` is a deallocation expression, `e` is an expression that dereferences a
|
||||
* pointer, and the `(dealloc1, e)` pair should be excluded by the `FlowFromFree` library.
|
||||
*/
|
||||
bindingset[dealloc1, e]
|
||||
predicate isExcludeFreeUsePair(DeallocationExpr dealloc1, Expr e) {
|
||||
// From https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmfreepagesfrommdl:
|
||||
// "After calling MmFreePagesFromMdl, the caller must also call ExFreePool
|
||||
// to release the memory that was allocated for the MDL structure."
|
||||
dealloc1.(FunctionCall).getTarget().hasGlobalName("MmFreePagesFromMdl") and
|
||||
isExFreePoolCall(_, e)
|
||||
}
|
||||
|
||||
module UseAfterFree = FlowFromFree<isUse/2, isExcludeFreeUsePair/2>;
|
||||
|
||||
from UseAfterFree::PathNode source, UseAfterFree::PathNode sink, DeallocationExpr dealloc
|
||||
from UseAfterFreeTrace::PathNode source, UseAfterFreeTrace::PathNode sink, DeallocationExpr dealloc
|
||||
where
|
||||
UseAfterFree::flowPath(source, sink) and
|
||||
UseAfterFreeTrace::flowPath(source, sink) and
|
||||
isFree(source.getNode(), _, _, dealloc)
|
||||
select sink.getNode(), source, sink, "Memory may have been previously freed by $@.", dealloc,
|
||||
dealloc.toString()
|
||||
|
||||
@@ -22,9 +22,6 @@ class SalMacro extends Macro {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalMacro */
|
||||
deprecated class SALMacro = SalMacro;
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
|
||||
|
||||
@@ -50,9 +47,6 @@ class SalAnnotation extends MacroInvocation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalAnnotation */
|
||||
deprecated class SALAnnotation = SalAnnotation;
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that the return value of a function should always be
|
||||
* checked.
|
||||
@@ -63,9 +57,6 @@ class SalCheckReturn extends SalAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalCheckReturn */
|
||||
deprecated class SALCheckReturn = SalCheckReturn;
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that a pointer variable or return value should not be
|
||||
* `NULL`.
|
||||
@@ -89,9 +80,6 @@ class SalNotNull extends SalAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalNotNull */
|
||||
deprecated class SALNotNull = SalNotNull;
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that a value may be `NULL`.
|
||||
*/
|
||||
@@ -105,9 +93,6 @@ class SalMaybeNull extends SalAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalMaybeNull */
|
||||
deprecated class SALMaybeNull = SalMaybeNull;
|
||||
|
||||
/**
|
||||
* A parameter annotated by one or more SAL annotations.
|
||||
*/
|
||||
@@ -124,9 +109,6 @@ class SalParameter extends Parameter {
|
||||
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalParameter */
|
||||
deprecated class SALParameter = SalParameter;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation details
|
||||
/**
|
||||
@@ -199,9 +181,6 @@ class SalElement extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalElement */
|
||||
deprecated class SALElement = SalElement;
|
||||
|
||||
/** Holds if `file` contains a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate containsSalAnnotation(File file) { any(SalAnnotation a).getFile() = file }
|
||||
|
||||
@@ -55,9 +55,6 @@ class SqlClientInfo extends SystemData {
|
||||
override predicate isSensitive() { any() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SqlClientInfo */
|
||||
deprecated class SQLClientInfo = SqlClientInfo;
|
||||
|
||||
private predicate sqlConnectInfo(FunctionCall source, Expr use) {
|
||||
(
|
||||
source.getTarget().hasName("mysql_connect") or
|
||||
@@ -77,9 +74,6 @@ class SqlConnectInfo extends SystemData {
|
||||
override predicate isSensitive() { any() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SqlConnectInfo */
|
||||
deprecated class SQLConnectInfo = SqlConnectInfo;
|
||||
|
||||
private predicate posixSystemInfo(FunctionCall source, DataFlow::Node use) {
|
||||
// size_t confstr(int name, char *buf, size_t len)
|
||||
// - various OS / system strings, such as the libc version
|
||||
|
||||
@@ -12,8 +12,6 @@ edges
|
||||
| A.cpp:31:20:31:20 | c | A.cpp:31:14:31:21 | call to B [c] |
|
||||
| A.cpp:41:5:41:6 | insert output argument | A.cpp:43:10:43:12 | *& ... |
|
||||
| A.cpp:41:15:41:21 | new | A.cpp:41:5:41:6 | insert output argument |
|
||||
| A.cpp:41:15:41:21 | new | A.cpp:41:5:41:6 | insert output argument |
|
||||
| A.cpp:41:15:41:21 | new | A.cpp:41:15:41:21 | new |
|
||||
| A.cpp:47:12:47:18 | new | A.cpp:48:20:48:20 | c |
|
||||
| A.cpp:48:12:48:18 | *call to make [c] | A.cpp:49:10:49:10 | *b [c] |
|
||||
| A.cpp:48:20:48:20 | c | A.cpp:29:23:29:23 | c |
|
||||
@@ -22,7 +20,6 @@ edges
|
||||
| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:10:56:10 | *b [c] |
|
||||
| A.cpp:55:12:55:19 | new | A.cpp:27:17:27:17 | c |
|
||||
| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | set output argument [c] |
|
||||
| A.cpp:55:12:55:19 | new | A.cpp:55:12:55:19 | new |
|
||||
| A.cpp:56:10:56:10 | *b [c] | A.cpp:28:8:28:10 | *this [c] |
|
||||
| A.cpp:56:10:56:10 | *b [c] | A.cpp:56:10:56:17 | call to get |
|
||||
| A.cpp:57:11:57:24 | *new [c] | A.cpp:28:8:28:10 | *this [c] |
|
||||
@@ -33,12 +30,10 @@ edges
|
||||
| A.cpp:57:17:57:23 | new | A.cpp:57:17:57:23 | new |
|
||||
| A.cpp:64:10:64:15 | *call to setOnB [c] | A.cpp:66:10:66:11 | *b2 [c] |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:64:10:64:15 | *call to setOnB [c] |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:64:21:64:28 | new |
|
||||
| A.cpp:64:21:64:28 | new | A.cpp:85:26:85:26 | c |
|
||||
| A.cpp:66:10:66:11 | *b2 [c] | A.cpp:66:10:66:14 | c |
|
||||
| A.cpp:73:10:73:19 | *call to setOnBWrap [c] | A.cpp:75:10:75:11 | *b2 [c] |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:73:10:73:19 | *call to setOnBWrap [c] |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:73:25:73:32 | new |
|
||||
| A.cpp:73:25:73:32 | new | A.cpp:78:27:78:27 | c |
|
||||
| A.cpp:75:10:75:11 | *b2 [c] | A.cpp:75:10:75:14 | c |
|
||||
| A.cpp:78:27:78:27 | c | A.cpp:81:21:81:21 | c |
|
||||
@@ -770,7 +765,6 @@ nodes
|
||||
| A.cpp:31:20:31:20 | c | semmle.label | c |
|
||||
| A.cpp:41:5:41:6 | insert output argument | semmle.label | insert output argument |
|
||||
| A.cpp:41:15:41:21 | new | semmle.label | new |
|
||||
| A.cpp:41:15:41:21 | new | semmle.label | new |
|
||||
| A.cpp:43:10:43:12 | *& ... | semmle.label | *& ... |
|
||||
| A.cpp:47:12:47:18 | new | semmle.label | new |
|
||||
| A.cpp:48:12:48:18 | *call to make [c] | semmle.label | *call to make [c] |
|
||||
@@ -779,7 +773,6 @@ nodes
|
||||
| A.cpp:49:10:49:13 | c | semmle.label | c |
|
||||
| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] |
|
||||
| A.cpp:55:12:55:19 | new | semmle.label | new |
|
||||
| A.cpp:55:12:55:19 | new | semmle.label | new |
|
||||
| A.cpp:56:10:56:10 | *b [c] | semmle.label | *b [c] |
|
||||
| A.cpp:56:10:56:17 | call to get | semmle.label | call to get |
|
||||
| A.cpp:57:10:57:32 | call to get | semmle.label | call to get |
|
||||
@@ -789,12 +782,10 @@ nodes
|
||||
| A.cpp:57:17:57:23 | new | semmle.label | new |
|
||||
| A.cpp:64:10:64:15 | *call to setOnB [c] | semmle.label | *call to setOnB [c] |
|
||||
| A.cpp:64:21:64:28 | new | semmle.label | new |
|
||||
| A.cpp:64:21:64:28 | new | semmle.label | new |
|
||||
| A.cpp:66:10:66:11 | *b2 [c] | semmle.label | *b2 [c] |
|
||||
| A.cpp:66:10:66:14 | c | semmle.label | c |
|
||||
| A.cpp:73:10:73:19 | *call to setOnBWrap [c] | semmle.label | *call to setOnBWrap [c] |
|
||||
| A.cpp:73:25:73:32 | new | semmle.label | new |
|
||||
| A.cpp:73:25:73:32 | new | semmle.label | new |
|
||||
| A.cpp:75:10:75:11 | *b2 [c] | semmle.label | *b2 [c] |
|
||||
| A.cpp:75:10:75:14 | c | semmle.label | c |
|
||||
| A.cpp:78:6:78:15 | **setOnBWrap [c] | semmle.label | **setOnBWrap [c] |
|
||||
@@ -1608,14 +1599,10 @@ subpaths
|
||||
| simple.cpp:84:14:84:20 | *this [f2, f1] | simple.cpp:78:9:78:15 | *this [f2, f1] | simple.cpp:78:9:78:15 | *getf2f1 | simple.cpp:84:14:84:20 | call to getf2f1 |
|
||||
#select
|
||||
| A.cpp:43:10:43:12 | *& ... | A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | *& ... | *& ... flows from $@ | A.cpp:41:15:41:21 | new | new |
|
||||
| A.cpp:43:10:43:12 | *& ... | A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | *& ... | *& ... flows from $@ | A.cpp:41:15:41:21 | new | new |
|
||||
| A.cpp:49:10:49:13 | c | A.cpp:47:12:47:18 | new | A.cpp:49:10:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new | new |
|
||||
| A.cpp:56:10:56:17 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:10:56:17 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
|
||||
| A.cpp:56:10:56:17 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:10:56:17 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
|
||||
| A.cpp:57:10:57:32 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:10:57:32 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new |
|
||||
| A.cpp:66:10:66:14 | c | A.cpp:64:21:64:28 | new | A.cpp:66:10:66:14 | c | c flows from $@ | A.cpp:64:21:64:28 | new | new |
|
||||
| A.cpp:66:10:66:14 | c | A.cpp:64:21:64:28 | new | A.cpp:66:10:66:14 | c | c flows from $@ | A.cpp:64:21:64:28 | new | new |
|
||||
| A.cpp:75:10:75:14 | c | A.cpp:73:25:73:32 | new | A.cpp:75:10:75:14 | c | c flows from $@ | A.cpp:73:25:73:32 | new | new |
|
||||
| A.cpp:75:10:75:14 | c | A.cpp:73:25:73:32 | new | A.cpp:75:10:75:14 | c | c flows from $@ | A.cpp:73:25:73:32 | new | new |
|
||||
| A.cpp:107:12:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new |
|
||||
| A.cpp:120:12:120:16 | a | A.cpp:98:12:98:18 | new | A.cpp:120:12:120:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new |
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
| concat.cpp:23:27:23:27 | call to operator+ | concat.cpp:23:22:23:25 | str1 | concat.cpp:23:22:23:31 | call to operator+ |
|
||||
| concat.cpp:23:27:23:27 | call to operator+ | concat.cpp:23:22:23:25 | str1 | concat.cpp:23:22:23:31 | call to operator+ |
|
||||
| concat.cpp:23:27:23:27 | call to operator+ | concat.cpp:23:22:23:25 | str1 | concat.cpp:23:27:23:27 | call to operator+ |
|
||||
| concat.cpp:23:27:23:27 | call to operator+ | concat.cpp:23:29:23:31 | | concat.cpp:23:22:23:31 | call to operator+ |
|
||||
| concat.cpp:23:27:23:27 | call to operator+ | concat.cpp:23:29:23:31 | | concat.cpp:23:22:23:31 | call to operator+ |
|
||||
| concat.cpp:23:27:23:27 | call to operator+ | concat.cpp:23:29:23:31 | | concat.cpp:23:27:23:27 | call to operator+ |
|
||||
| concat.cpp:23:33:23:33 | call to operator+ | concat.cpp:23:35:23:38 | str2 | concat.cpp:23:22:23:38 | call to operator+ |
|
||||
| concat.cpp:23:33:23:33 | call to operator+ | concat.cpp:23:35:23:38 | str2 | concat.cpp:23:22:23:38 | call to operator+ |
|
||||
| concat.cpp:23:33:23:33 | call to operator+ | concat.cpp:23:35:23:38 | str2 | concat.cpp:23:33:23:33 | call to operator+ |
|
||||
| concat.cpp:23:40:23:40 | call to operator+ | concat.cpp:23:42:23:45 | str3 | concat.cpp:23:40:23:40 | call to operator+ |
|
||||
| concat.cpp:47:8:47:8 | call to operator<< | concat.cpp:47:11:47:14 | str1 | concat.cpp:47:8:47:17 | call to operator<< |
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
| test_free.cpp:216:10:216:10 | a |
|
||||
| test_free.cpp:220:10:220:10 | a |
|
||||
| test_free.cpp:227:24:227:45 | memory_descriptor_list |
|
||||
| test_free.cpp:228:16:228:37 | memory_descriptor_list |
|
||||
| test_free.cpp:233:14:233:15 | * ... |
|
||||
| test_free.cpp:239:14:239:15 | * ... |
|
||||
| test_free.cpp:245:10:245:11 | * ... |
|
||||
|
||||
@@ -116,9 +116,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
|
||||
string? IBuildActions.GetEnvironmentVariable(string name)
|
||||
{
|
||||
if (!GetEnvironmentVariable.TryGetValue(name, out var ret))
|
||||
throw new ArgumentException("Missing GetEnvironmentVariable " + name);
|
||||
|
||||
GetEnvironmentVariable.TryGetValue(name, out var ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Semmle.Autobuild.CSharp
|
||||
/// </summary>
|
||||
public class CSharpAutobuildOptions : AutobuildOptionsShared
|
||||
{
|
||||
private const string buildModeEnvironmentVariable = "CODEQL_EXTRACTOR_CSHARP_BUILD_MODE";
|
||||
private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
|
||||
|
||||
public bool Buildless { get; }
|
||||
@@ -25,7 +26,8 @@ namespace Semmle.Autobuild.CSharp
|
||||
public CSharpAutobuildOptions(IBuildActions actions) : base(actions)
|
||||
{
|
||||
Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
|
||||
actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
|
||||
actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false) ||
|
||||
actions.GetEnvironmentVariable(buildModeEnvironmentVariable)?.ToLower() == "none";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -650,12 +650,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
}
|
||||
|
||||
private bool RestoreProject(string project, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, string? pathToNugetConfig = null) =>
|
||||
dotnet.RestoreProjectToDirectory(project, packageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching, out assets, pathToNugetConfig);
|
||||
|
||||
private bool RestoreSolution(string solution, out IEnumerable<string> projects, out IEnumerable<string> assets) =>
|
||||
dotnet.RestoreSolutionToDirectory(solution, packageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching: true, out projects, out assets);
|
||||
|
||||
/// <summary>
|
||||
/// Executes `dotnet restore` on all solution files in solutions.
|
||||
/// As opposed to RestoreProjects this is not run in parallel using PLINQ
|
||||
@@ -670,7 +664,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
var assetFiles = new List<string>();
|
||||
var projects = solutions.SelectMany(solution =>
|
||||
{
|
||||
RestoreSolution(solution, out var restoredProjects, out var a);
|
||||
dotnet.RestoreSolutionToDirectory(solution, packageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching: true, out var restoredProjects, out var a);
|
||||
assetFiles.AddRange(a);
|
||||
return restoredProjects;
|
||||
});
|
||||
@@ -689,7 +683,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
var assetFiles = new List<string>();
|
||||
Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, project =>
|
||||
{
|
||||
RestoreProject(project, forceDotnetRefAssemblyFetching: true, out var a);
|
||||
dotnet.RestoreProjectToDirectory(project, packageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching: true, out var a, out var _);
|
||||
assetFiles.AddRange(a);
|
||||
});
|
||||
assets = assetFiles;
|
||||
@@ -736,11 +730,21 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
return;
|
||||
}
|
||||
|
||||
dotnet.RestoreProjectToDirectory(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching: false, out var _, pathToNugetConfig: nugetConfig);
|
||||
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
|
||||
success = dotnet.RestoreProjectToDirectory(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching: false, out var _, out var outputLines, pathToNugetConfig: nugetConfig);
|
||||
if (!success)
|
||||
{
|
||||
progressMonitor.FailedToRestoreNugetPackage(package);
|
||||
if (outputLines?.Any(s => s.Contains("NU1301")) == true)
|
||||
{
|
||||
// Restore could not be completed because the listed source is unavailable. Try without the nuget.config:
|
||||
success = dotnet.RestoreProjectToDirectory(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, forceDotnetRefAssemblyFetching: false, out var _, out var _, pathToNugetConfig: null, force: true);
|
||||
}
|
||||
|
||||
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
|
||||
|
||||
if (!success)
|
||||
{
|
||||
progressMonitor.FailedToRestoreNugetPackage(package);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private static IEnumerable<string> GetRestoredProjects(IEnumerable<string> lines) =>
|
||||
GetFirstGroupOnMatch(RestoredProjectRegex(), lines);
|
||||
|
||||
public bool RestoreProjectToDirectory(string projectFile, string packageDirectory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, string? pathToNugetConfig = null)
|
||||
public bool RestoreProjectToDirectory(string projectFile, string packageDirectory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, out IList<string> outputLines, string? pathToNugetConfig = null, bool force = false)
|
||||
{
|
||||
var args = GetRestoreArgs(projectFile, packageDirectory, forceDotnetRefAssemblyFetching);
|
||||
if (pathToNugetConfig != null)
|
||||
@@ -80,8 +80,13 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
args += $" --configfile \"{pathToNugetConfig}\"";
|
||||
}
|
||||
|
||||
var success = dotnetCliInvoker.RunCommand(args, out var output);
|
||||
assets = success ? GetAssetsFilePaths(output) : Array.Empty<string>();
|
||||
if (force)
|
||||
{
|
||||
args += " --force";
|
||||
}
|
||||
|
||||
var success = dotnetCliInvoker.RunCommand(args, out outputLines);
|
||||
assets = success ? GetAssetsFilePaths(outputLines) : Array.Empty<string>();
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
internal interface IDotNet
|
||||
{
|
||||
bool RestoreProjectToDirectory(string project, string directory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, string? pathToNugetConfig = null);
|
||||
bool RestoreProjectToDirectory(string project, string directory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, out IList<string> outputLines, string? pathToNugetConfig = null, bool force = false);
|
||||
bool RestoreSolutionToDirectory(string solutionFile, string packageDirectory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> projects, out IEnumerable<string> assets);
|
||||
bool New(string folder);
|
||||
bool AddPackage(string folder, string package);
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Semmle.Extraction.Tests
|
||||
var dotnet = MakeDotnet(dotnetCliInvoker);
|
||||
|
||||
// Execute
|
||||
dotnet.RestoreProjectToDirectory("myproject.csproj", "mypackages", false, out var assets);
|
||||
dotnet.RestoreProjectToDirectory("myproject.csproj", "mypackages", false, out var assets, out var _);
|
||||
|
||||
// Verify
|
||||
var lastArgs = dotnetCliInvoker.GetLastArgs();
|
||||
@@ -116,7 +116,7 @@ namespace Semmle.Extraction.Tests
|
||||
var dotnet = MakeDotnet(dotnetCliInvoker);
|
||||
|
||||
// Execute
|
||||
dotnet.RestoreProjectToDirectory("myproject.csproj", "mypackages", false, out var assets, "myconfig.config");
|
||||
dotnet.RestoreProjectToDirectory("myproject.csproj", "mypackages", false, out var assets, out var _, pathToNugetConfig: "myconfig.config");
|
||||
|
||||
// Verify
|
||||
var lastArgs = dotnetCliInvoker.GetLastArgs();
|
||||
@@ -126,6 +126,24 @@ namespace Semmle.Extraction.Tests
|
||||
Assert.Contains("/path/to/project2.assets.json", assets);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestDotnetRestoreProjectToDirectory3()
|
||||
{
|
||||
// Setup
|
||||
var dotnetCliInvoker = new DotNetCliInvokerStub(MakeDotnetRestoreOutput());
|
||||
var dotnet = MakeDotnet(dotnetCliInvoker);
|
||||
|
||||
// Execute
|
||||
dotnet.RestoreProjectToDirectory("myproject.csproj", "mypackages", false, out var assets, out var _, pathToNugetConfig: "myconfig.config", force: true);
|
||||
|
||||
// Verify
|
||||
var lastArgs = dotnetCliInvoker.GetLastArgs();
|
||||
Assert.Equal("restore --no-dependencies \"myproject.csproj\" --packages \"mypackages\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile \"myconfig.config\" --force", lastArgs);
|
||||
Assert.Equal(2, assets.Count());
|
||||
Assert.Contains("/path/to/project.assets.json", assets);
|
||||
Assert.Contains("/path/to/project2.assets.json", assets);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestDotnetRestoreSolutionToDirectory1()
|
||||
{
|
||||
|
||||
@@ -19,9 +19,10 @@ namespace Semmle.Extraction.Tests
|
||||
|
||||
public bool New(string folder) => true;
|
||||
|
||||
public bool RestoreProjectToDirectory(string project, string directory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, string? pathToNugetConfig = null)
|
||||
public bool RestoreProjectToDirectory(string project, string directory, bool forceDotnetRefAssemblyFetching, out IEnumerable<string> assets, out IList<string> outputLines, string? pathToNugetConfig = null, bool force = false)
|
||||
{
|
||||
assets = Array.Empty<string>();
|
||||
outputLines = Array.Empty<string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| newtonsoft.json/13.0.3/lib/net6.0/Newtonsoft.Json.dll |
|
||||
@@ -0,0 +1,11 @@
|
||||
import csharp
|
||||
|
||||
private string getPath(Assembly a) {
|
||||
not a.getCompilation().getOutputAssembly() = a and
|
||||
exists(string s | s = a.getFile().getAbsolutePath() |
|
||||
result = s.substring(s.indexOf("newtonsoft.json"), s.length())
|
||||
)
|
||||
}
|
||||
|
||||
from Assembly a
|
||||
select getPath(a)
|
||||
@@ -0,0 +1,6 @@
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="x" value="https://abc.abc/packages/" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
|
||||
<RemoveDir Directories=".\bin" />
|
||||
<RemoveDir Directories=".\obj" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "proj", "proj\proj.csproj", "{6ED00460-7666-4AE9-A405-4B6C8B02279A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4ED55A1C-066C-43DF-B32E-7EAA035985EE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,3 @@
|
||||
from create_database_utils import *
|
||||
|
||||
run_codeql_database_create([], lang="csharp", extra_args=["--extractor-option=buildless=true", "--extractor-option=cil=false"])
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added a new library `semmle.code.csharp.security.dataflow.flowsources.FlowSources`, which provides a new class `ThreatModelFlowSource`. The `ThreatModelFlowSource` class can be used to include sources which match the current *threat model* configuration.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted many deprecated predicates and classes with uppercase `SSL`, `XML`, `URI`, `SSA` etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `getALocalFlowSucc` predicate and `TaintType` class from the dataflow library.
|
||||
* Deleted the deprecated `Newobj` and `Rethrow` classes, use `NewObj` and `ReThrow` instead.
|
||||
* Deleted the deprecated `getAFirstRead`, `hasAdjacentReads`, `lastRefBeforeRedef`, and `hasLastInputRef` predicates from the SSA library.
|
||||
* Deleted the deprecated `getAReachableRead` predicate from the `AssignableRead` and `VariableRead` classes.
|
||||
* Deleted the deprecated `hasQualifiedName` predicate from the `NamedElement` class.
|
||||
@@ -39,9 +39,6 @@ predicate maybeUsedInFnvFunction(Variable v, Operation xor, Operation mul, LoopS
|
||||
loop.getAChild*() = xor.getEnclosingStmt()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for maybeUsedInFnvFunction */
|
||||
deprecated predicate maybeUsedInFNVFunction = maybeUsedInFnvFunction/4;
|
||||
|
||||
/**
|
||||
* Holds if the arguments are used in a way that resembles an Elf-Hash hash function
|
||||
* where there is a loop statement `loop` where the variable `v` is used in an xor `xor` expression
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/threat-models: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
dataExtensions:
|
||||
|
||||
@@ -89,16 +89,10 @@ class FormsElement extends XmlElement {
|
||||
result = this.getAttribute("requireSSL").getValue().trim().toLowerCase()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getRequireSsl */
|
||||
deprecated string getRequireSSL() { result = this.getRequireSsl() }
|
||||
|
||||
/**
|
||||
* Holds if `requireSSL` value is true.
|
||||
*/
|
||||
predicate isRequireSsl() { this.getRequireSsl() = "true" }
|
||||
|
||||
/** DEPRECATED: Alias for isRequireSsl */
|
||||
deprecated predicate isRequireSSL() { this.isRequireSsl() }
|
||||
}
|
||||
|
||||
/** A `<httpCookies>` tag in an ASP.NET configuration file. */
|
||||
@@ -124,9 +118,6 @@ class HttpCookiesElement extends XmlElement {
|
||||
result = this.getAttribute("requireSSL").getValue().trim().toLowerCase()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getRequireSsl */
|
||||
deprecated string getRequireSSL() { result = this.getRequireSsl() }
|
||||
|
||||
/**
|
||||
* Holds if there is any chance that `requireSSL` is set to `true` either globally or for Forms.
|
||||
*/
|
||||
@@ -136,9 +127,6 @@ class HttpCookiesElement extends XmlElement {
|
||||
not this.getRequireSsl() = "false" and // not set all, i.e. default
|
||||
exists(FormsElement forms | forms.getFile() = this.getFile() | forms.isRequireSsl())
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isRequireSsl */
|
||||
deprecated predicate isRequireSSL() { this.isRequireSsl() }
|
||||
}
|
||||
|
||||
/** A `Transform` attribute in a Web.config transformation file. */
|
||||
|
||||
@@ -16,23 +16,6 @@ class DataFlowNode extends @cil_dataflow_node {
|
||||
/** Gets the type of this data flow node. */
|
||||
Type getType() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this node flows to `sink` in one step.
|
||||
* `tt` is the tainting that occurs during this step.
|
||||
*/
|
||||
deprecated predicate getALocalFlowSucc(DataFlowNode sink, TaintType tt) {
|
||||
localExactStep(this, sink) and tt = TExactValue()
|
||||
or
|
||||
localTaintStep(this, sink) and tt = TTaintedValue()
|
||||
}
|
||||
|
||||
deprecated private predicate flowsToStep(DataFlowNode sink) {
|
||||
this.getALocalFlowSucc(sink, TExactValue())
|
||||
}
|
||||
|
||||
/** Holds if this node flows to `sink` in zero or more steps. */
|
||||
deprecated predicate flowsTo(DataFlowNode sink) { this.flowsToStep*(sink) }
|
||||
|
||||
/** Gets the method that contains this dataflow node. */
|
||||
Method getMethod() { none() }
|
||||
|
||||
@@ -40,77 +23,6 @@ class DataFlowNode extends @cil_dataflow_node {
|
||||
Location getLocation() { none() }
|
||||
}
|
||||
|
||||
deprecated private newtype TTaintType =
|
||||
TExactValue() or
|
||||
TTaintedValue()
|
||||
|
||||
/** Describes how data is tainted. */
|
||||
deprecated class TaintType extends TTaintType {
|
||||
string toString() {
|
||||
this = TExactValue() and result = "exact"
|
||||
or
|
||||
this = TTaintedValue() and result = "tainted"
|
||||
}
|
||||
}
|
||||
|
||||
/** A taint type where the data is untainted. */
|
||||
deprecated class Untainted extends TaintType, TExactValue { }
|
||||
|
||||
/** A taint type where the data is tainted. */
|
||||
deprecated class Tainted extends TaintType, TTaintedValue { }
|
||||
|
||||
deprecated private predicate localFlowPhiInput(DataFlowNode input, Ssa::PhiNode phi) {
|
||||
exists(Ssa::Definition def, BasicBlock bb, int i | phi.hasLastInputRef(def, bb, i) |
|
||||
def.definesAt(_, bb, i) and
|
||||
input = def.getVariableUpdate().getSource()
|
||||
or
|
||||
input =
|
||||
any(ReadAccess ra |
|
||||
bb.getNode(i) = ra and
|
||||
ra.getTarget() = def.getSourceVariable()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Ssa::PhiNode mid, BasicBlock bb, int i |
|
||||
localFlowPhiInput(input, mid) and
|
||||
phi.hasLastInputRef(mid, bb, i) and
|
||||
mid.definesAt(_, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate localExactStep(DataFlowNode src, DataFlowNode sink) {
|
||||
src = sink.(Opcodes::Dup).getAnOperand()
|
||||
or
|
||||
exists(Ssa::Definition def, VariableUpdate vu |
|
||||
vu = def.getVariableUpdate() and
|
||||
src = vu.getSource() and
|
||||
sink = def.getAFirstRead()
|
||||
)
|
||||
or
|
||||
any(Ssa::Definition def).hasAdjacentReads(src, sink)
|
||||
or
|
||||
exists(Ssa::PhiNode phi |
|
||||
localFlowPhiInput(src, phi) and
|
||||
sink = phi.getAFirstRead()
|
||||
)
|
||||
or
|
||||
src = sink.(Conversion).getExpr()
|
||||
or
|
||||
src = sink.(WriteAccess).getExpr()
|
||||
or
|
||||
src = sink.(Method).getAnImplementation().getAnInstruction().(Return)
|
||||
or
|
||||
src = sink.(Return).getExpr()
|
||||
or
|
||||
src = sink.(ConditionalBranch).getAnOperand()
|
||||
}
|
||||
|
||||
deprecated private predicate localTaintStep(DataFlowNode src, DataFlowNode sink) {
|
||||
src = sink.(BinaryArithmeticExpr).getAnOperand() or
|
||||
src = sink.(Opcodes::Neg).getOperand() or
|
||||
src = sink.(UnaryBitwiseOperation).getOperand()
|
||||
}
|
||||
|
||||
/** A node that updates a variable. */
|
||||
abstract class VariableUpdate extends DataFlowNode {
|
||||
/** Gets the value assigned, if any. */
|
||||
|
||||
@@ -788,9 +788,6 @@ module Opcodes {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NewObj */
|
||||
deprecated class Newobj = NewObj;
|
||||
|
||||
/** An `initobj` instruction. */
|
||||
class Initobj extends Instruction, @cil_initobj {
|
||||
override string getOpcodeName() { result = "initobj" }
|
||||
@@ -854,9 +851,6 @@ module Opcodes {
|
||||
override string getOpcodeName() { result = "rethrow" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ReThrow */
|
||||
deprecated class Rethrow = ReThrow;
|
||||
|
||||
/** A `ldlen` instruction. */
|
||||
class Ldlen extends UnaryExpr, @cil_ldlen {
|
||||
override string getOpcodeName() { result = "ldlen" }
|
||||
|
||||
@@ -23,14 +23,6 @@ module Ssa {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a first read of this SSA definition. */
|
||||
deprecated final ReadAccess getAFirstRead() { result = SsaImpl::getAFirstRead(this) }
|
||||
|
||||
/** Holds if `first` and `second` are adjacent reads of this SSA definition. */
|
||||
deprecated final predicate hasAdjacentReads(ReadAccess first, ReadAccess second) {
|
||||
SsaImpl::hasAdjacentReads(this, first, second)
|
||||
}
|
||||
|
||||
private Definition getAPhiInput() { result = this.(PhiNode).getAnInput() }
|
||||
|
||||
/**
|
||||
@@ -52,15 +44,5 @@ module Ssa {
|
||||
|
||||
/** Gets an input to this phi node. */
|
||||
final Definition getAnInput() { result = SsaImpl::getAPhiInput(this) }
|
||||
|
||||
/**
|
||||
* Holds if if `def` is an input to this phi node, and a reference to `def` at
|
||||
* index `i` in basic block `bb` can reach this phi node without going through
|
||||
* other references.
|
||||
*/
|
||||
deprecated final predicate hasLastInputRef(Definition def, BasicBlock bb, int i) {
|
||||
SsaImpl::lastRefRedef(def, bb, i, this) and
|
||||
def = SsaImpl::getAPhiInput(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,26 +77,6 @@ import Cached
|
||||
|
||||
private module Deprecated {
|
||||
private import CIL
|
||||
|
||||
deprecated ReadAccess getAFirstRead(Definition def) {
|
||||
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
|
||||
def.definesAt(_, bb1, i1) and
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
result = bb2.getNode(i2)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate hasAdjacentReads(Definition def, ReadAccess first, ReadAccess second) {
|
||||
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
|
||||
first = bb1.getNode(i1) and
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
second = bb2.getNode(i2)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate lastRefBeforeRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
lastRefRedef(def, bb, i, next)
|
||||
}
|
||||
}
|
||||
|
||||
import Deprecated
|
||||
|
||||
@@ -117,15 +117,6 @@ class AssignableRead extends AssignableAccess {
|
||||
cfn = this.getAnAdjacentReadSameVar()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reachable read of the same underlying assignable. That is, a read
|
||||
* that can be reached from this read, and which is guaranteed to read the
|
||||
* same value.
|
||||
*
|
||||
* This is the transitive closure of `getANextRead()`.
|
||||
*/
|
||||
deprecated AssignableRead getAReachableRead() { result = this.getANextRead+() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,15 +480,6 @@ class AssignableDefinition extends TAssignableDefinition {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reachable read of the same underlying assignable. That is, a read
|
||||
* that can be reached from this definition, and which is guaranteed to read
|
||||
* the value assigned in this definition.
|
||||
*
|
||||
* This is the equivalent with `getAFirstRead().getANextRead*()`.
|
||||
*/
|
||||
deprecated AssignableRead getAReachableRead() { result = this.getAFirstRead().getANextRead*() }
|
||||
|
||||
/** Gets a textual representation of this assignable definition. */
|
||||
string toString() { none() }
|
||||
|
||||
|
||||
@@ -32,9 +32,6 @@ class XmlLocatable extends @xmllocatable, TXmlLocatable {
|
||||
string toString() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlLocatable */
|
||||
deprecated class XMLLocatable = XmlLocatable;
|
||||
|
||||
/**
|
||||
* An `XmlParent` is either an `XmlElement` or an `XmlFile`,
|
||||
* both of which can contain other elements.
|
||||
@@ -95,9 +92,6 @@ class XmlParent extends @xmlparent {
|
||||
string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlParent */
|
||||
deprecated class XMLParent = XmlParent;
|
||||
|
||||
/** An XML file. */
|
||||
class XmlFile extends XmlParent, File {
|
||||
XmlFile() { xmlEncoding(this, _) }
|
||||
@@ -119,14 +113,8 @@ class XmlFile extends XmlParent, File {
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XmlDtd getADtd() { xmlDTDs(result, _, _, _, this) }
|
||||
|
||||
/** DEPRECATED: Alias for getADtd */
|
||||
deprecated XmlDtd getADTD() { result = this.getADtd() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlFile */
|
||||
deprecated class XMLFile = XmlFile;
|
||||
|
||||
/**
|
||||
* An XML document type definition (DTD).
|
||||
*
|
||||
@@ -163,9 +151,6 @@ class XmlDtd extends XmlLocatable, @xmldtd {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlDtd */
|
||||
deprecated class XMLDTD = XmlDtd;
|
||||
|
||||
/**
|
||||
* An XML element in an XML file.
|
||||
*
|
||||
@@ -221,9 +206,6 @@ class XmlElement extends @xmlelement, XmlParent, XmlLocatable {
|
||||
override string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlElement */
|
||||
deprecated class XMLElement = XmlElement;
|
||||
|
||||
/**
|
||||
* An attribute that occurs inside an XML element.
|
||||
*
|
||||
@@ -254,9 +236,6 @@ class XmlAttribute extends @xmlattribute, XmlLocatable {
|
||||
override string toString() { result = this.getName() + "=" + this.getValue() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlAttribute */
|
||||
deprecated class XMLAttribute = XmlAttribute;
|
||||
|
||||
/**
|
||||
* A namespace used in an XML file.
|
||||
*
|
||||
@@ -273,9 +252,6 @@ class XmlNamespace extends XmlLocatable, @xmlnamespace {
|
||||
/** Gets the URI of this namespace. */
|
||||
string getUri() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** DEPRECATED: Alias for getUri */
|
||||
deprecated string getURI() { result = this.getUri() }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
@@ -286,9 +262,6 @@ class XmlNamespace extends XmlLocatable, @xmlnamespace {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlNamespace */
|
||||
deprecated class XMLNamespace = XmlNamespace;
|
||||
|
||||
/**
|
||||
* A comment in an XML file.
|
||||
*
|
||||
@@ -309,9 +282,6 @@ class XmlComment extends @xmlcomment, XmlLocatable {
|
||||
override string toString() { result = this.getText() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlComment */
|
||||
deprecated class XMLComment = XmlComment;
|
||||
|
||||
/**
|
||||
* A sequence of characters that occurs between opening and
|
||||
* closing tags of an XML element, excluding other elements.
|
||||
@@ -335,6 +305,3 @@ class XmlCharacters extends @xmlcharacters, XmlLocatable {
|
||||
/** Gets a printable representation of this XML character sequence. */
|
||||
override string toString() { result = this.getCharacters() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XmlCharacters */
|
||||
deprecated class XMLCharacters = XmlCharacters;
|
||||
|
||||
@@ -173,10 +173,6 @@ class VariableAccess extends AssignableAccess, @variable_access_expr {
|
||||
*/
|
||||
class VariableRead extends VariableAccess, AssignableRead {
|
||||
override VariableRead getANextRead() { result = AssignableRead.super.getANextRead() }
|
||||
|
||||
deprecated override VariableRead getAReachableRead() {
|
||||
result = AssignableRead.super.getAReachableRead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,10 +197,6 @@ class LocalScopeVariableAccess extends VariableAccess, @local_scope_variable_acc
|
||||
*/
|
||||
class LocalScopeVariableRead extends LocalScopeVariableAccess, VariableRead {
|
||||
override LocalScopeVariableRead getANextRead() { result = VariableRead.super.getANextRead() }
|
||||
|
||||
deprecated override LocalScopeVariableRead getAReachableRead() {
|
||||
result = VariableRead.super.getAReachableRead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,10 +235,6 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr {
|
||||
*/
|
||||
class ParameterRead extends ParameterAccess, LocalScopeVariableRead {
|
||||
override ParameterRead getANextRead() { result = LocalScopeVariableRead.super.getANextRead() }
|
||||
|
||||
deprecated override ParameterRead getAReachableRead() {
|
||||
result = LocalScopeVariableRead.super.getAReachableRead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,10 +286,6 @@ class LocalVariableAccess extends LocalScopeVariableAccess, @local_variable_acce
|
||||
*/
|
||||
class LocalVariableRead extends LocalVariableAccess, LocalScopeVariableRead {
|
||||
override LocalVariableRead getANextRead() { result = LocalScopeVariableRead.super.getANextRead() }
|
||||
|
||||
deprecated override LocalVariableRead getAReachableRead() {
|
||||
result = LocalScopeVariableRead.super.getAReachableRead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,10 +427,6 @@ class PropertyAccess extends AssignableMemberAccess, PropertyAccessExpr {
|
||||
*/
|
||||
class PropertyRead extends PropertyAccess, AssignableRead {
|
||||
override PropertyRead getANextRead() { result = AssignableRead.super.getANextRead() }
|
||||
|
||||
deprecated override PropertyRead getAReachableRead() {
|
||||
result = AssignableRead.super.getAReachableRead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -584,10 +564,6 @@ class IndexerAccess extends AssignableMemberAccess, ElementAccess, IndexerAccess
|
||||
*/
|
||||
class IndexerRead extends IndexerAccess, ElementRead {
|
||||
override IndexerRead getANextRead() { result = ElementRead.super.getANextRead() }
|
||||
|
||||
deprecated override IndexerRead getAReachableRead() {
|
||||
result = ElementRead.super.getAReachableRead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ private import semmle.code.csharp.frameworks.Sql
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Public
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Stored as Stored
|
||||
|
||||
/**
|
||||
* Definitions relating to the `System.ComponentModel.DataAnnotations`
|
||||
@@ -44,7 +45,7 @@ module EntityFramework {
|
||||
}
|
||||
|
||||
/** A taint source where the data has come from a mapped property stored in the database. */
|
||||
class StoredFlowSource extends DataFlow::Node {
|
||||
class StoredFlowSource extends Stored::DatabaseInputSource {
|
||||
StoredFlowSource() {
|
||||
this.asExpr() = any(PropertyRead read | read.getTarget() instanceof MappedProperty)
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class ValidFormatString extends StringLiteral {
|
||||
result = this.getValue().regexpFind(getValidFormatTokenRegex(), _, outPosition)
|
||||
}
|
||||
|
||||
/**Gets the insert number at the given position in the string. */
|
||||
/** Gets the insert number at the given position in the string. */
|
||||
int getInsert(int position) {
|
||||
result = this.getToken(position).regexpCapture(getFormatInsertRegex(), 1).toInt()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import csharp
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.Collections
|
||||
private import semmle.code.csharp.frameworks.Sql
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Stored as Stored
|
||||
|
||||
/** Definitions relating to the `NHibernate` package. */
|
||||
module NHibernate {
|
||||
@@ -86,7 +87,7 @@ module NHibernate {
|
||||
}
|
||||
|
||||
/** A taint source where the data has come from a mapped property stored in the database. */
|
||||
class StoredFlowSource extends DataFlow::Node {
|
||||
class StoredFlowSource extends Stored::DatabaseInputSource {
|
||||
StoredFlowSource() {
|
||||
this.asExpr() = any(PropertyRead read | read.getTarget() instanceof MappedProperty)
|
||||
}
|
||||
|
||||
@@ -123,9 +123,6 @@ class MicrosoftOwinIOwinRequestClass extends Class {
|
||||
result = this.getAProperty() and
|
||||
result.hasName("Uri")
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getUriProperty */
|
||||
deprecated Property getURIProperty() { result = this.getUriProperty() }
|
||||
}
|
||||
|
||||
/** A `Microsoft.Owin.*String` class. */
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
import csharp
|
||||
private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm
|
||||
|
||||
/** Array of type Byte */
|
||||
deprecated class ByteArray extends ArrayType {
|
||||
ByteArray() { this.getElementType() instanceof ByteType }
|
||||
}
|
||||
|
||||
/** Abstract class for all sources of keys */
|
||||
abstract class KeySource extends DataFlow::Node { }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
private import semmle.code.csharp.frameworks.Format
|
||||
private import semmle.code.csharp.frameworks.system.Web
|
||||
private import semmle.code.csharp.frameworks.system.web.Mvc
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
@@ -163,6 +164,36 @@ class ConcatenationSanitizer extends Sanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A string interpolation expression, where the first part (before any inserts) of the
|
||||
* expression contains the character "?".
|
||||
*
|
||||
* This is considered a sanitizer by the same reasoning as `ConcatenationSanitizer`.
|
||||
*/
|
||||
private class InterpolationSanitizer extends Sanitizer {
|
||||
InterpolationSanitizer() {
|
||||
this.getExpr().(InterpolatedStringExpr).getText(0).getValue().matches("%?%")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `string.Format`, where the format expression (before any inserts)
|
||||
* contains the character "?".
|
||||
*
|
||||
* This is considered a sanitizer by the same reasoning as `ConcatenationSanitizer`.
|
||||
*/
|
||||
private class StringFormatSanitizer extends Sanitizer {
|
||||
StringFormatSanitizer() {
|
||||
exists(FormatCall c, Expr e, int index, string format |
|
||||
c = this.getExpr() and e = c.getFormatExpr()
|
||||
|
|
||||
format = e.(StringLiteral).getValue() and
|
||||
exists(format.regexpFind("\\{[0-9]+\\}", 0, index)) and
|
||||
format.substring(0, index).matches("%?%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to an URL encoder. */
|
||||
class UrlEncodeSanitizer extends Sanitizer {
|
||||
UrlEncodeSanitizer() { this.getExpr() instanceof UrlSanitizedExpr }
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/** Provides classes representing various flow sources for taint tracking. */
|
||||
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import codeql.threatmodels.ThreatModels
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Stored
|
||||
|
||||
/**
|
||||
* A data flow source.
|
||||
*/
|
||||
abstract class SourceNode extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a string that represents the source kind with respect to threat modeling.
|
||||
*/
|
||||
abstract string getThreatModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of data flow sources that respects the
|
||||
* current threat model configuration.
|
||||
*/
|
||||
class ThreatModelFlowSource extends DataFlow::Node {
|
||||
ThreatModelFlowSource() {
|
||||
exists(string kind |
|
||||
// Specific threat model.
|
||||
currentThreatModel(kind) and
|
||||
(this.(SourceNode).getThreatModel() = kind or sourceNode(this, kind))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,14 @@
|
||||
import csharp
|
||||
private import semmle.code.csharp.frameworks.system.windows.Forms
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.FlowSources
|
||||
|
||||
/** A data flow source of local data. */
|
||||
abstract class LocalFlowSource extends DataFlow::Node {
|
||||
abstract class LocalFlowSource extends SourceNode {
|
||||
/** Gets a string that describes the type of this local flow source. */
|
||||
abstract string getSourceType();
|
||||
|
||||
override string getThreatModel() { result = "local" }
|
||||
}
|
||||
|
||||
private class ExternalLocalFlowSource extends LocalFlowSource {
|
||||
|
||||
@@ -13,11 +13,14 @@ private import semmle.code.csharp.frameworks.WCF
|
||||
private import semmle.code.csharp.frameworks.microsoft.Owin
|
||||
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.FlowSources
|
||||
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
abstract class RemoteFlowSource extends SourceNode {
|
||||
/** Gets a string that describes the type of this remote flow source. */
|
||||
abstract string getSourceType();
|
||||
|
||||
override string getThreatModel() { result = "remote" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,15 +9,25 @@ private import semmle.code.csharp.frameworks.system.data.Entity
|
||||
private import semmle.code.csharp.frameworks.EntityFramework
|
||||
private import semmle.code.csharp.frameworks.NHibernate
|
||||
private import semmle.code.csharp.frameworks.Sql
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.FlowSources
|
||||
|
||||
/** A data flow source of stored user input. */
|
||||
abstract class StoredFlowSource extends DataFlow::Node { }
|
||||
abstract class StoredFlowSource extends SourceNode {
|
||||
override string getThreatModel() { result = "local" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node with input from a database.
|
||||
*/
|
||||
abstract class DatabaseInputSource extends StoredFlowSource {
|
||||
override string getThreatModel() { result = "database" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that has a type of `DbRawSqlQuery`, representing the result of an Entity Framework
|
||||
* SqlQuery.
|
||||
*/
|
||||
class DbRawSqlStoredFlowSource extends StoredFlowSource {
|
||||
class DbRawSqlStoredFlowSource extends DatabaseInputSource {
|
||||
DbRawSqlStoredFlowSource() {
|
||||
this.asExpr().getType() instanceof SystemDataEntityInfrastructure::DbRawSqlQuery
|
||||
}
|
||||
@@ -27,14 +37,14 @@ class DbRawSqlStoredFlowSource extends StoredFlowSource {
|
||||
* An expression that has a type of `DbDataReader` or a sub-class, representing the result of a
|
||||
* data command.
|
||||
*/
|
||||
class DbDataReaderStoredFlowSource extends StoredFlowSource {
|
||||
class DbDataReaderStoredFlowSource extends DatabaseInputSource {
|
||||
DbDataReaderStoredFlowSource() {
|
||||
this.asExpr().getType() = any(SystemDataCommon::DbDataReader dataReader).getASubType*()
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that accesses a method of `DbDataReader` or a sub-class. */
|
||||
class DbDataReaderMethodStoredFlowSource extends StoredFlowSource {
|
||||
class DbDataReaderMethodStoredFlowSource extends DatabaseInputSource {
|
||||
DbDataReaderMethodStoredFlowSource() {
|
||||
this.asExpr().(MethodCall).getTarget().getDeclaringType() =
|
||||
any(SystemDataCommon::DbDataReader dataReader).getASubType*()
|
||||
@@ -42,15 +52,19 @@ class DbDataReaderMethodStoredFlowSource extends StoredFlowSource {
|
||||
}
|
||||
|
||||
/** An expression that accesses a property of `DbDataReader` or a sub-class. */
|
||||
class DbDataReaderPropertyStoredFlowSource extends StoredFlowSource {
|
||||
class DbDataReaderPropertyStoredFlowSource extends DatabaseInputSource {
|
||||
DbDataReaderPropertyStoredFlowSource() {
|
||||
this.asExpr().(PropertyAccess).getTarget().getDeclaringType() =
|
||||
any(SystemDataCommon::DbDataReader dataReader).getASubType*()
|
||||
}
|
||||
}
|
||||
|
||||
/** A read of a mapped property. */
|
||||
class ORMMappedProperty extends StoredFlowSource {
|
||||
/**
|
||||
* DEPRECATED: Use `EntityFramework::StoredFlowSource` and `NHibernate::StoredFlowSource` instead.
|
||||
*
|
||||
* A read of a mapped property.
|
||||
*/
|
||||
deprecated class ORMMappedProperty extends DataFlow::Node {
|
||||
ORMMappedProperty() {
|
||||
this instanceof EntityFramework::StoredFlowSource or
|
||||
this instanceof NHibernate::StoredFlowSource
|
||||
@@ -60,4 +74,6 @@ class ORMMappedProperty extends StoredFlowSource {
|
||||
/** A file stream source is considered a stored flow source. */
|
||||
class FileStreamStoredFlowSource extends StoredFlowSource {
|
||||
FileStreamStoredFlowSource() { sourceNode(this, "file") }
|
||||
|
||||
override string getThreatModel() { result = "file" }
|
||||
}
|
||||
|
||||
@@ -118,15 +118,6 @@ class NamedElement extends Element, @dotnet_named_element {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `hasQualifiedName/2` instead.
|
||||
* Holds if this element has qualified name `qualifiedName`, for example
|
||||
* `System.Console.WriteLine`.
|
||||
*/
|
||||
deprecated final predicate hasQualifiedName(string qualifiedName) {
|
||||
qualifiedName = this.getQualifiedName()
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `hasFullyQualifiedName` instead.
|
||||
*
|
||||
|
||||
@@ -220,6 +220,10 @@
|
||||
<k>@function_pointer_type</k>
|
||||
<v>47824</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@inline_array_type</k>
|
||||
<v>0</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@uint_ptr_type</k>
|
||||
<v>0</v>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added string interpolation expressions and `string.Format` as possible sanitizers for the `cs/web/unvalidated-url-redirection` query.
|
||||
@@ -17,6 +17,3 @@ module Ssa {
|
||||
|
||||
predicate hasUnreachedInstruction(IRFunctionBase irFunc) { none() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Ssa */
|
||||
deprecated module SSA = Ssa;
|
||||
|
||||
@@ -2,6 +2,3 @@ import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import experimental.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSsa
|
||||
import AliasedSSAStub as AliasedSsa
|
||||
|
||||
/** DEPRECATED: Alias for AliasedSsa */
|
||||
deprecated module AliasedSSA = AliasedSsa;
|
||||
|
||||
@@ -1068,6 +1068,3 @@ module Ssa {
|
||||
|
||||
predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for Ssa */
|
||||
deprecated module SSA = Ssa;
|
||||
|
||||
@@ -4,13 +4,6 @@ import experimental.ir.implementation.raw.internal.reachability.Dominance as Dom
|
||||
import experimental.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import experimental.ir.implementation.internal.TInstruction::UnaliasedSsaInstructions as SsaInstructions
|
||||
|
||||
/** DEPRECATED: Alias for SsaInstructions */
|
||||
deprecated module SSAInstructions = SsaInstructions;
|
||||
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
import experimental.ir.implementation.internal.TOperand::UnaliasedSsaOperands as SsaOperands
|
||||
|
||||
/** DEPRECATED: Alias for SsaOperands */
|
||||
deprecated module SSAOperands = SsaOperands;
|
||||
|
||||
68
csharp/ql/test/library-tests/dataflow/threat-models/Test.cs
Normal file
68
csharp/ql/test/library-tests/dataflow/threat-models/Test.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace My.Qltest
|
||||
{
|
||||
public class Test
|
||||
{
|
||||
private TestSources Sources = new TestSources();
|
||||
|
||||
private SqlConnection Connection => throw null;
|
||||
|
||||
private string BytesToString(byte[] bytes)
|
||||
{
|
||||
// Encode bytes to a UTF8 string.
|
||||
return System.Text.Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public void M1()
|
||||
{
|
||||
// Only a source if "remote" is a selected threat model.
|
||||
// This is included in the "default" threat model.
|
||||
using TcpClient client = new TcpClient("localhost", 1234);
|
||||
using NetworkStream stream = client.GetStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead = stream.Read(buffer, 0, buffer.Length);
|
||||
|
||||
// SQL sink
|
||||
var command = new SqlCommand("SELECT * FROM Users WHERE Username = '" + BytesToString(buffer) + "'", Connection);
|
||||
}
|
||||
|
||||
public void M2()
|
||||
{
|
||||
// Only a source if "database" is a selected threat model.
|
||||
string result = Sources.ExecuteQuery("SELECT * FROM foo");
|
||||
|
||||
// SQL sink
|
||||
var command = new SqlCommand("SELECT * FROM Users WHERE Username = '" + result + "'", Connection);
|
||||
}
|
||||
|
||||
public void M3()
|
||||
{
|
||||
// Only a source if "environment" is a selected threat model.
|
||||
string result = Sources.ReadEnv("foo");
|
||||
|
||||
// SQL sink
|
||||
var command = new SqlCommand("SELECT * FROM Users WHERE Username = '" + result + "'", Connection);
|
||||
|
||||
}
|
||||
|
||||
public void M4()
|
||||
{
|
||||
// Only a source if "custom" is a selected threat model.
|
||||
string result = Sources.GetCustom("foo");
|
||||
|
||||
// SQL sink
|
||||
var command = new SqlCommand("SELECT * FROM Users WHERE Username = '" + result + "'", Connection);
|
||||
}
|
||||
|
||||
public void M5()
|
||||
{
|
||||
// Only a source if "commandargs" is a selected threat model.
|
||||
string result = Sources.GetCliArg(0);
|
||||
|
||||
// SQL sink
|
||||
var command = new SqlCommand("SELECT * FROM Users WHERE Username = '" + result + "'", Connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
csharp/ql/test/library-tests/dataflow/threat-models/Test.qll
Normal file
12
csharp/ql/test/library-tests/dataflow/threat-models/Test.qll
Normal file
@@ -0,0 +1,12 @@
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dataflow.DataFlow
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.FlowSources
|
||||
|
||||
private module ThreatModelConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, _) }
|
||||
}
|
||||
|
||||
module ThreatModel = TaintTracking::Global<ThreatModelConfig>;
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace My.Qltest
|
||||
{
|
||||
|
||||
public class TestSources
|
||||
{
|
||||
public string ExecuteQuery(string query)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string ReadEnv(string env)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetCustom(string s)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetCliArg(int i)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/System.Data.SqlClient/4.8.5/System.Data.SqlClient.csproj
|
||||
@@ -0,0 +1,23 @@
|
||||
edges
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:25:29:25:34 | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
nodes
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | semmle.label | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:20:15:61 | call to method GetString : String | semmle.label | call to method GetString : String |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | semmle.label | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | semmle.label | call to method GetStream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | semmle.label | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | semmle.label | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:42:28:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | semmle.label | call to method BytesToString : String |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | semmle.label | access to local variable buffer : Byte[] [element] : Object |
|
||||
subpaths
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
#select
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:28:42:28:111 | ... + ... |
|
||||
@@ -0,0 +1,15 @@
|
||||
extensions:
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/threat-models
|
||||
extensible: threatModelConfiguration
|
||||
data: []
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["My.Qltest", "TestSources", False, "ExecuteQuery", "(System.String)", "", "ReturnValue", "database", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "ReadEnv", "(System.String)", "", "ReturnValue", "environment", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCustom", "(System.String)", "", "ReturnValue", "custom", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCliArg", "(System.Int32)", "", "ReturnValue", "commandargs", "manual"]
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* This is a dataflow test using the "default" threat model.
|
||||
*/
|
||||
|
||||
import Test
|
||||
import ThreatModel::PathGraph
|
||||
|
||||
from ThreatModel::PathNode source, ThreatModel::PathNode sink
|
||||
where ThreatModel::flowPath(source, sink)
|
||||
select source, sink
|
||||
@@ -0,0 +1,27 @@
|
||||
edges
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:25:29:25:34 | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
nodes
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | semmle.label | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:20:15:61 | call to method GetString : String | semmle.label | call to method GetString : String |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | semmle.label | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | semmle.label | call to method GetStream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | semmle.label | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | semmle.label | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:42:28:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | semmle.label | call to method BytesToString : String |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | semmle.label | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | semmle.label | call to method ExecuteQuery : String |
|
||||
| Test.cs:37:42:37:96 | ... + ... | semmle.label | ... + ... |
|
||||
subpaths
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
#select
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/threat-models
|
||||
extensible: threatModelConfiguration
|
||||
data:
|
||||
- ["database", true, 0]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["My.Qltest", "TestSources", False, "ExecuteQuery", "(System.String)", "", "ReturnValue", "database", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "ReadEnv", "(System.String)", "", "ReturnValue", "environment", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCustom", "(System.String)", "", "ReturnValue", "custom", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCliArg", "(System.Int32)", "", "ReturnValue", "commandargs", "manual"]
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This is a dataflow test using the "default" threat model with the
|
||||
* addition of "database".
|
||||
*/
|
||||
|
||||
import Test
|
||||
import ThreatModel::PathGraph
|
||||
|
||||
from ThreatModel::PathNode source, ThreatModel::PathNode sink
|
||||
where ThreatModel::flowPath(source, sink)
|
||||
select source, sink
|
||||
@@ -0,0 +1,35 @@
|
||||
edges
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:25:29:25:34 | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | Test.cs:46:42:46:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
nodes
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | semmle.label | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:20:15:61 | call to method GetString : String | semmle.label | call to method GetString : String |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | semmle.label | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | semmle.label | call to method GetStream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | semmle.label | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | semmle.label | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:42:28:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | semmle.label | call to method BytesToString : String |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | semmle.label | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | semmle.label | call to method ExecuteQuery : String |
|
||||
| Test.cs:37:42:37:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | semmle.label | call to method ReadEnv : String |
|
||||
| Test.cs:46:42:46:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | semmle.label | call to method GetCliArg : String |
|
||||
| Test.cs:65:42:65:96 | ... + ... | semmle.label | ... + ... |
|
||||
subpaths
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
#select
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | Test.cs:46:42:46:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
@@ -0,0 +1,17 @@
|
||||
extensions:
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/threat-models
|
||||
extensible: threatModelConfiguration
|
||||
data:
|
||||
- ["local", true, 0]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["My.Qltest", "TestSources", False, "ExecuteQuery", "(System.String)", "", "ReturnValue", "database", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "ReadEnv", "(System.String)", "", "ReturnValue", "environment", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCustom", "(System.String)", "", "ReturnValue", "custom", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCliArg", "(System.Int32)", "", "ReturnValue", "commandargs", "manual"]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This is a dataflow test using the "default" threat model with the
|
||||
* addition of the threat model group "local".
|
||||
*/
|
||||
|
||||
import Test
|
||||
import ThreatModel::PathGraph
|
||||
|
||||
from ThreatModel::PathNode source, ThreatModel::PathNode sink
|
||||
where ThreatModel::flowPath(source, sink)
|
||||
select source, sink
|
||||
@@ -0,0 +1,39 @@
|
||||
edges
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:25:29:25:34 | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | Test.cs:46:42:46:96 | ... + ... |
|
||||
| Test.cs:53:29:53:52 | call to method GetCustom : String | Test.cs:56:42:56:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
nodes
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | semmle.label | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:20:15:61 | call to method GetString : String | semmle.label | call to method GetString : String |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | semmle.label | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | semmle.label | call to method GetStream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | semmle.label | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | semmle.label | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:42:28:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | semmle.label | call to method BytesToString : String |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | semmle.label | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | semmle.label | call to method ExecuteQuery : String |
|
||||
| Test.cs:37:42:37:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | semmle.label | call to method ReadEnv : String |
|
||||
| Test.cs:46:42:46:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:53:29:53:52 | call to method GetCustom : String | semmle.label | call to method GetCustom : String |
|
||||
| Test.cs:56:42:56:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | semmle.label | call to method GetCliArg : String |
|
||||
| Test.cs:65:42:65:96 | ... + ... | semmle.label | ... + ... |
|
||||
subpaths
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
#select
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | Test.cs:46:42:46:96 | ... + ... |
|
||||
| Test.cs:53:29:53:52 | call to method GetCustom : String | Test.cs:56:42:56:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
@@ -0,0 +1,17 @@
|
||||
extensions:
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/threat-models
|
||||
extensible: threatModelConfiguration
|
||||
data:
|
||||
- ["all", true, 0]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["My.Qltest", "TestSources", False, "ExecuteQuery", "(System.String)", "", "ReturnValue", "database", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "ReadEnv", "(System.String)", "", "ReturnValue", "environment", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCustom", "(System.String)", "", "ReturnValue", "custom", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCliArg", "(System.Int32)", "", "ReturnValue", "commandargs", "manual"]
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* This is a dataflow test using "all" threat models.
|
||||
*/
|
||||
|
||||
import Test
|
||||
import ThreatModel::PathGraph
|
||||
|
||||
from ThreatModel::PathNode source, ThreatModel::PathNode sink
|
||||
where ThreatModel::flowPath(source, sink)
|
||||
select source, sink
|
||||
@@ -0,0 +1,31 @@
|
||||
edges
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:25:29:25:34 | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | Test.cs:46:42:46:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
nodes
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | semmle.label | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:20:15:61 | call to method GetString : String | semmle.label | call to method GetString : String |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | semmle.label | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | semmle.label | call to method GetStream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | semmle.label | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | semmle.label | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:42:28:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | semmle.label | call to method BytesToString : String |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | semmle.label | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | semmle.label | call to method ReadEnv : String |
|
||||
| Test.cs:46:42:46:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | semmle.label | call to method GetCliArg : String |
|
||||
| Test.cs:65:42:65:96 | ... + ... | semmle.label | ... + ... |
|
||||
subpaths
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
#select
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:43:29:43:50 | call to method ReadEnv : String | Test.cs:46:42:46:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/threat-models
|
||||
extensible: threatModelConfiguration
|
||||
data:
|
||||
- ["environment", true, 0]
|
||||
- ["commandargs", true, 0]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["My.Qltest", "TestSources", False, "ExecuteQuery", "(System.String)", "", "ReturnValue", "database", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "ReadEnv", "(System.String)", "", "ReturnValue", "environment", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCustom", "(System.String)", "", "ReturnValue", "custom", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCliArg", "(System.Int32)", "", "ReturnValue", "commandargs", "manual"]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This is a dataflow test using the "default" threat model with the
|
||||
* addition of "environment" and "commandargs".
|
||||
*/
|
||||
|
||||
import Test
|
||||
import ThreatModel::PathGraph
|
||||
|
||||
from ThreatModel::PathNode source, ThreatModel::PathNode sink
|
||||
where ThreatModel::flowPath(source, sink)
|
||||
select source, sink
|
||||
@@ -0,0 +1,31 @@
|
||||
edges
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:25:29:25:34 | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
nodes
|
||||
| Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | semmle.label | bytes : Byte[] [element] : Object |
|
||||
| Test.cs:15:20:15:61 | call to method GetString : String | semmle.label | call to method GetString : String |
|
||||
| Test.cs:15:56:15:60 | access to parameter bytes : Byte[] [element] : Object | semmle.label | access to parameter bytes : Byte[] [element] : Object |
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | semmle.label | call to method GetStream : NetworkStream |
|
||||
| Test.cs:25:29:25:34 | access to local variable stream : NetworkStream | semmle.label | access to local variable stream : NetworkStream |
|
||||
| Test.cs:25:41:25:46 | [post] access to local variable buffer : Byte[] [element] : Object | semmle.label | [post] access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:28:42:28:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:28:85:28:105 | call to method BytesToString : String | semmle.label | call to method BytesToString : String |
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | semmle.label | access to local variable buffer : Byte[] [element] : Object |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | semmle.label | call to method ExecuteQuery : String |
|
||||
| Test.cs:37:42:37:96 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | semmle.label | call to method GetCliArg : String |
|
||||
| Test.cs:65:42:65:96 | ... + ... | semmle.label | ... + ... |
|
||||
subpaths
|
||||
| Test.cs:28:99:28:104 | access to local variable buffer : Byte[] [element] : Object | Test.cs:12:45:12:49 | bytes : Byte[] [element] : Object | Test.cs:15:20:15:61 | call to method GetString : String | Test.cs:28:85:28:105 | call to method BytesToString : String |
|
||||
#select
|
||||
| Test.cs:23:42:23:59 | call to method GetStream : NetworkStream | Test.cs:28:42:28:111 | ... + ... |
|
||||
| Test.cs:34:29:34:69 | call to method ExecuteQuery : String | Test.cs:37:42:37:96 | ... + ... |
|
||||
| Test.cs:62:29:62:48 | call to method GetCliArg : String | Test.cs:65:42:65:96 | ... + ... |
|
||||
@@ -0,0 +1,17 @@
|
||||
extensions:
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/threat-models
|
||||
extensible: threatModelConfiguration
|
||||
data:
|
||||
- ["local", true, 0]
|
||||
- ["environment", false, 1]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["My.Qltest", "TestSources", False, "ExecuteQuery", "(System.String)", "", "ReturnValue", "database", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "ReadEnv", "(System.String)", "", "ReturnValue", "environment", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCustom", "(System.String)", "", "ReturnValue", "custom", "manual"]
|
||||
- ["My.Qltest", "TestSources", False, "GetCliArg", "(System.Int32)", "", "ReturnValue", "commandargs", "manual"]
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This is a dataflow test using the "default" threat model with the
|
||||
* addition of the threat model group "local", but without the
|
||||
* "environment" threat model.
|
||||
*/
|
||||
|
||||
import Test
|
||||
import ThreatModel::PathGraph
|
||||
|
||||
from ThreatModel::PathNode source, ThreatModel::PathNode sink
|
||||
where ThreatModel::flowPath(source, sink)
|
||||
select source, sink
|
||||
@@ -41,7 +41,7 @@ public class UrlRedirectHandler : IHttpHandler
|
||||
// GOOD: Redirecting to the RawUrl only reloads the current Url
|
||||
ctx.Response.Redirect(ctx.Request.RawUrl);
|
||||
|
||||
// GOOD: The attacker can only control the parameters, not the locaiton
|
||||
// GOOD: The attacker can only control the parameters, not the location
|
||||
ctx.Response.Redirect("foo.asp?param=" + url);
|
||||
|
||||
// BAD: Using Transfer with unvalidated user input
|
||||
@@ -56,6 +56,24 @@ public class UrlRedirectHandler : IHttpHandler
|
||||
{
|
||||
ctx.Response.Redirect(url3);
|
||||
}
|
||||
|
||||
// GOOD: The attacker can only control the parameters, not the location
|
||||
ctx.Response.Redirect($"foo.asp?param={url}");
|
||||
|
||||
// BAD: The attacker can control the location
|
||||
ctx.Response.Redirect($"{url}.asp?param=foo");
|
||||
|
||||
// GOOD: The attacker can only control the parameters, not the location
|
||||
ctx.Response.Redirect(string.Format("foo.asp?param={0}", url));
|
||||
|
||||
// BAD: The attacker can control the location
|
||||
ctx.Response.Redirect(string.Format("{0}.asp?param=foo", url));
|
||||
|
||||
// GOOD: The attacker can only control the parameters, not the location
|
||||
ctx.Response.Redirect(string.Format("foo.asp?{1}param={0}", url, url));
|
||||
|
||||
// BAD: The attacker can control the location
|
||||
ctx.Response.Redirect(string.Format("{1}.asp?{0}param=foo", url, url));
|
||||
}
|
||||
|
||||
// Implementation as recommended by Microsoft.
|
||||
|
||||
@@ -2,9 +2,20 @@ edges
|
||||
| UrlRedirect.cs:13:31:13:53 | access to property QueryString : NameValueCollection | UrlRedirect.cs:13:31:13:61 | access to indexer |
|
||||
| UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:23:22:23:52 | access to indexer : String |
|
||||
| UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:48:29:48:31 | access to local variable url |
|
||||
| UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:64:31:64:52 | $"..." |
|
||||
| UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:70:66:70:68 | access to local variable url : String |
|
||||
| UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:76:69:76:71 | access to local variable url : String |
|
||||
| UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:76:74:76:76 | access to local variable url : String |
|
||||
| UrlRedirect.cs:23:22:23:52 | access to indexer : String | UrlRedirect.cs:48:29:48:31 | access to local variable url |
|
||||
| UrlRedirect.cs:23:22:23:52 | access to indexer : String | UrlRedirect.cs:64:31:64:52 | $"..." |
|
||||
| UrlRedirect.cs:23:22:23:52 | access to indexer : String | UrlRedirect.cs:70:66:70:68 | access to local variable url : String |
|
||||
| UrlRedirect.cs:23:22:23:52 | access to indexer : String | UrlRedirect.cs:76:69:76:71 | access to local variable url : String |
|
||||
| UrlRedirect.cs:23:22:23:52 | access to indexer : String | UrlRedirect.cs:76:74:76:76 | access to local variable url : String |
|
||||
| UrlRedirect.cs:38:44:38:66 | access to property QueryString : NameValueCollection | UrlRedirect.cs:38:44:38:74 | access to indexer |
|
||||
| UrlRedirect.cs:39:47:39:69 | access to property QueryString : NameValueCollection | UrlRedirect.cs:39:47:39:77 | access to indexer |
|
||||
| UrlRedirect.cs:70:66:70:68 | access to local variable url : String | UrlRedirect.cs:70:31:70:69 | call to method Format |
|
||||
| UrlRedirect.cs:76:69:76:71 | access to local variable url : String | UrlRedirect.cs:76:31:76:77 | call to method Format |
|
||||
| UrlRedirect.cs:76:74:76:76 | access to local variable url : String | UrlRedirect.cs:76:31:76:77 | call to method Format |
|
||||
| UrlRedirectCore.cs:13:44:13:48 | value : String | UrlRedirectCore.cs:16:22:16:26 | access to parameter value |
|
||||
| UrlRedirectCore.cs:13:44:13:48 | value : String | UrlRedirectCore.cs:19:44:19:48 | call to operator implicit conversion |
|
||||
| UrlRedirectCore.cs:13:44:13:48 | value : String | UrlRedirectCore.cs:25:46:25:50 | call to operator implicit conversion |
|
||||
@@ -26,6 +37,12 @@ nodes
|
||||
| UrlRedirect.cs:39:47:39:69 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection |
|
||||
| UrlRedirect.cs:39:47:39:77 | access to indexer | semmle.label | access to indexer |
|
||||
| UrlRedirect.cs:48:29:48:31 | access to local variable url | semmle.label | access to local variable url |
|
||||
| UrlRedirect.cs:64:31:64:52 | $"..." | semmle.label | $"..." |
|
||||
| UrlRedirect.cs:70:31:70:69 | call to method Format | semmle.label | call to method Format |
|
||||
| UrlRedirect.cs:70:66:70:68 | access to local variable url : String | semmle.label | access to local variable url : String |
|
||||
| UrlRedirect.cs:76:31:76:77 | call to method Format | semmle.label | call to method Format |
|
||||
| UrlRedirect.cs:76:69:76:71 | access to local variable url : String | semmle.label | access to local variable url : String |
|
||||
| UrlRedirect.cs:76:74:76:76 | access to local variable url : String | semmle.label | access to local variable url : String |
|
||||
| UrlRedirectCore.cs:13:44:13:48 | value : String | semmle.label | value : String |
|
||||
| UrlRedirectCore.cs:16:22:16:26 | access to parameter value | semmle.label | access to parameter value |
|
||||
| UrlRedirectCore.cs:19:44:19:48 | call to operator implicit conversion | semmle.label | call to operator implicit conversion |
|
||||
@@ -45,6 +62,9 @@ subpaths
|
||||
| UrlRedirect.cs:38:44:38:74 | access to indexer | UrlRedirect.cs:38:44:38:66 | access to property QueryString : NameValueCollection | UrlRedirect.cs:38:44:38:74 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:38:44:38:66 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:39:47:39:77 | access to indexer | UrlRedirect.cs:39:47:39:69 | access to property QueryString : NameValueCollection | UrlRedirect.cs:39:47:39:77 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:39:47:39:69 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:48:29:48:31 | access to local variable url | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:48:29:48:31 | access to local variable url | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:64:31:64:52 | $"..." | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:64:31:64:52 | $"..." | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:70:31:70:69 | call to method Format | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:70:31:70:69 | call to method Format | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:76:31:76:77 | call to method Format | UrlRedirect.cs:23:22:23:44 | access to property QueryString : NameValueCollection | UrlRedirect.cs:76:31:76:77 | call to method Format | Untrusted URL redirection due to $@. | UrlRedirect.cs:23:22:23:44 | access to property QueryString | user-provided value |
|
||||
| UrlRedirectCore.cs:16:22:16:26 | access to parameter value | UrlRedirectCore.cs:13:44:13:48 | value : String | UrlRedirectCore.cs:16:22:16:26 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:13:44:13:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:19:44:19:48 | call to operator implicit conversion | UrlRedirectCore.cs:13:44:13:48 | value : String | UrlRedirectCore.cs:19:44:19:48 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:13:44:13:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:25:46:25:50 | call to operator implicit conversion | UrlRedirectCore.cs:13:44:13:48 | value : String | UrlRedirectCore.cs:25:46:25:50 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:13:44:13:48 | value | user-provided value |
|
||||
|
||||
@@ -51,6 +51,7 @@ and the CodeQL library pack ``codeql/go-all`` (`changelog <https://github.com/gi
|
||||
:widths: auto
|
||||
|
||||
Name, Category
|
||||
`AWS Lambda <https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html>`_, Serverless framework
|
||||
`beego <https://beego.me/>`_, Web/logging/database framework
|
||||
`Chi <https://github.com/go-chi/chi>`_, Web framework
|
||||
Couchbase (`gocb <https://github.com/couchbase/gocb>`_ and `go-couchbase <http://www.github.com/couchbase/go-couchbase>`_), Database
|
||||
@@ -144,6 +145,7 @@ and the CodeQL library pack ``codeql/javascript-all`` (`changelog <https://githu
|
||||
Name, Category
|
||||
angular (modern version), HTML framework
|
||||
angular.js (legacy version), HTML framework
|
||||
AWS Lambda, Serverless framework
|
||||
axios, Network communicator
|
||||
browser, Runtime environment
|
||||
EJS, templating language
|
||||
@@ -191,7 +193,7 @@ and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.co
|
||||
:widths: auto
|
||||
|
||||
Name, Category
|
||||
AWS Lambda, Web framework
|
||||
AWS Lambda, Serverless framework
|
||||
aiohttp.web, Web framework
|
||||
Django, Web framework
|
||||
djangorestframework, Web framework
|
||||
|
||||
283
go/extractor/autobuilder/build-environment.go
Normal file
283
go/extractor/autobuilder/build-environment.go
Normal file
@@ -0,0 +1,283 @@
|
||||
package autobuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/github/codeql-go/extractor/diagnostics"
|
||||
"github.com/github/codeql-go/extractor/project"
|
||||
"github.com/github/codeql-go/extractor/toolchain"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const minGoVersion = "1.11"
|
||||
const maxGoVersion = "1.21"
|
||||
|
||||
type versionInfo struct {
|
||||
goModVersion string // The version of Go found in the go directive in the `go.mod` file.
|
||||
goModVersionFound bool // Whether a `go` directive was found in the `go.mod` file.
|
||||
goEnvVersion string // The version of Go found in the environment.
|
||||
goEnvVersionFound bool // Whether an installation of Go was found in the environment.
|
||||
}
|
||||
|
||||
func (v versionInfo) String() string {
|
||||
return fmt.Sprintf(
|
||||
"go.mod version: %s, go.mod directive found: %t, go env version: %s, go installation found: %t",
|
||||
v.goModVersion, v.goModVersionFound, v.goEnvVersion, v.goEnvVersionFound)
|
||||
}
|
||||
|
||||
// Check if `version` is lower than `minGoVersion`. Note that for this comparison we ignore the
|
||||
// patch part of the version, so 1.20.1 and 1.20 are considered equal.
|
||||
func belowSupportedRange(version string) bool {
|
||||
return semver.Compare(semver.MajorMinor("v"+version), "v"+minGoVersion) < 0
|
||||
}
|
||||
|
||||
// Check if `version` is higher than `maxGoVersion`. Note that for this comparison we ignore the
|
||||
// patch part of the version, so 1.20.1 and 1.20 are considered equal.
|
||||
func aboveSupportedRange(version string) bool {
|
||||
return semver.Compare(semver.MajorMinor("v"+version), "v"+maxGoVersion) > 0
|
||||
}
|
||||
|
||||
// Check if `version` is lower than `minGoVersion` or higher than `maxGoVersion`. Note that for
|
||||
// this comparison we ignore the patch part of the version, so 1.20.1 and 1.20 are considered
|
||||
// equal.
|
||||
func outsideSupportedRange(version string) bool {
|
||||
return belowSupportedRange(version) || aboveSupportedRange(version)
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersionFound` is false, emit a diagnostic and return the version to install,
|
||||
// or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionNotFound(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// There is no Go version installed in the environment. We have no indication which version
|
||||
// was intended to be used to build this project. Go versions are generally backwards
|
||||
// compatible, so we install the maximum supported version.
|
||||
msg = "No version of Go installed and no `go.mod` file found. Requesting the maximum " +
|
||||
"supported version of Go (" + maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitNoGoModAndNoGoEnv(msg)
|
||||
} else if outsideSupportedRange(v.goEnvVersion) {
|
||||
// The Go version installed in the environment is not supported. We have no indication
|
||||
// which version was intended to be used to build this project. Go versions are generally
|
||||
// backwards compatible, so we install the maximum supported version.
|
||||
msg = "No `go.mod` file found. The version of Go installed in the environment (" +
|
||||
v.goEnvVersion + ") is outside of the supported range (" + minGoVersion + "-" +
|
||||
maxGoVersion + "). Requesting the maximum supported version of Go (" + maxGoVersion +
|
||||
")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitNoGoModAndGoEnvUnsupported(msg)
|
||||
} else {
|
||||
// The version of Go that is installed is supported. We have no indication which version
|
||||
// was intended to be used to build this project. We assume that the installed version is
|
||||
// suitable and do not install a version of Go.
|
||||
msg = "No `go.mod` file found. Version " + v.goEnvVersion + " installed in the " +
|
||||
"environment is supported. Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitNoGoModAndGoEnvSupported(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersion` is above the supported range, emit a diagnostic and return the
|
||||
// version to install, or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionTooHigh(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// The version in the `go.mod` file is above the supported range. There is no Go version
|
||||
// installed. We install the maximum supported version as a best effort.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). No version of Go installed. Requesting the maximum supported version of Go (" +
|
||||
maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitGoModVersionTooHighAndNoGoEnv(msg)
|
||||
} else if aboveSupportedRange(v.goEnvVersion) {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is above the supported range. We do not install a version of Go.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionTooHigh(msg)
|
||||
} else if belowSupportedRange(v.goEnvVersion) {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is below the supported range. We install the maximum supported version as
|
||||
// a best effort.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). Requesting the maximum supported version of Go (" + maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionTooLow(msg)
|
||||
} else if semver.Compare("v"+maxGoVersion, "v"+v.goEnvVersion) > 0 {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is supported and below the maximum supported version. We install the
|
||||
// maximum supported version as a best effort.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is below the maximum supported version (" + maxGoVersion +
|
||||
"). Requesting the maximum supported version of Go (" + maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionBelowMax(msg)
|
||||
} else {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is the maximum supported version. We do not install a version of Go.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is the maximum supported version (" + maxGoVersion +
|
||||
"). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionMax(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersion` is below the supported range, emit a diagnostic and return the
|
||||
// version to install, or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionTooLow(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// There is no Go version installed. The version in the `go.mod` file is below the
|
||||
// supported range. Go versions are generally backwards compatible, so we install the
|
||||
// minimum supported version.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). No version of Go installed. Requesting the minimum supported version of Go (" +
|
||||
minGoVersion + ")."
|
||||
version = minGoVersion
|
||||
diagnostics.EmitGoModVersionTooLowAndNoGoEnv(msg)
|
||||
} else if outsideSupportedRange(v.goEnvVersion) {
|
||||
// The version of Go that is installed is outside of the supported range. The version
|
||||
// in the `go.mod` file is below the supported range. Go versions are generally
|
||||
// backwards compatible, so we install the minimum supported version.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + "). " +
|
||||
"Requesting the minimum supported version of Go (" + minGoVersion + ")."
|
||||
version = minGoVersion
|
||||
diagnostics.EmitGoModVersionTooLowAndEnvVersionUnsupported(msg)
|
||||
} else {
|
||||
// The version of Go that is installed is supported. The version in the `go.mod` file is
|
||||
// below the supported range. We do not install a version of Go.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is supported and is high enough for the version found in the `go.mod` file (" +
|
||||
v.goModVersion + "). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionTooLowAndEnvVersionSupported(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersion` is in the supported range, emit a diagnostic and return the version
|
||||
// to install, or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionSupported(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// There is no Go version installed. The version in the `go.mod` file is supported.
|
||||
// We install the version from the `go.mod` file.
|
||||
msg = "No version of Go installed. Requesting the version of Go found in the `go.mod` " +
|
||||
"file (" + v.goModVersion + ")."
|
||||
version = v.goModVersion
|
||||
diagnostics.EmitGoModVersionSupportedAndNoGoEnv(msg)
|
||||
} else if outsideSupportedRange(v.goEnvVersion) {
|
||||
// The version of Go that is installed is outside of the supported range. The version in
|
||||
// the `go.mod` file is supported. We install the version from the `go.mod` file.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + "). " +
|
||||
"Requesting the version of Go from the `go.mod` file (" +
|
||||
v.goModVersion + ")."
|
||||
version = v.goModVersion
|
||||
diagnostics.EmitGoModVersionSupportedAndGoEnvUnsupported(msg)
|
||||
} else if semver.Compare("v"+v.goModVersion, "v"+v.goEnvVersion) > 0 {
|
||||
// The version of Go that is installed is supported. The version in the `go.mod` file is
|
||||
// supported and is higher than the version that is installed. We install the version from
|
||||
// the `go.mod` file.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is lower than the version found in the `go.mod` file (" + v.goModVersion +
|
||||
"). Requesting the version of Go from the `go.mod` file (" + v.goModVersion + ")."
|
||||
version = v.goModVersion
|
||||
diagnostics.EmitGoModVersionSupportedHigherGoEnv(msg)
|
||||
} else {
|
||||
// The version of Go that is installed is supported. The version in the `go.mod` file is
|
||||
// supported and is lower than or equal to the version that is installed. We do not install
|
||||
// a version of Go.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is supported and is high enough for the version found in the `go.mod` file (" +
|
||||
v.goModVersion + "). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionSupportedLowerEqualGoEnv(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Check the versions of Go found in the environment and in the `go.mod` file, and return a
|
||||
// version to install. If the version is the empty string then no installation is required.
|
||||
// We never return a version of Go that is outside of the supported range.
|
||||
//
|
||||
// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+
|
||||
// | Found in go.mod > | *None* | *Below min supported* | *In supported range* | *Above max supported |
|
||||
// | Installed \/ | | | | |
|
||||
// |-----------------------|-----------------------|-----------------------|-----------------------------------------------------|------------------------------------------------|
|
||||
// | *None* | Install max supported | Install min supported | Install version from go.mod | Install max supported |
|
||||
// | *Below min supported* | Install max supported | Install min supported | Install version from go.mod | Install max supported |
|
||||
// | *In supported range* | No action | No action | Install version from go.mod if newer than installed | Install max supported if newer than installed |
|
||||
// | *Above max supported* | Install max supported | Install min supported | Install version from go.mod | No action |
|
||||
// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+
|
||||
func getVersionToInstall(v versionInfo) (msg, version string) {
|
||||
if !v.goModVersionFound {
|
||||
return getVersionWhenGoModVersionNotFound(v)
|
||||
}
|
||||
|
||||
if aboveSupportedRange(v.goModVersion) {
|
||||
return getVersionWhenGoModVersionTooHigh(v)
|
||||
}
|
||||
|
||||
if belowSupportedRange(v.goModVersion) {
|
||||
return getVersionWhenGoModVersionTooLow(v)
|
||||
}
|
||||
|
||||
return getVersionWhenGoModVersionSupported(v)
|
||||
}
|
||||
|
||||
// Output some JSON to stdout specifying the version of Go to install, unless `version` is the
|
||||
// empty string.
|
||||
func outputEnvironmentJson(version string) {
|
||||
var content string
|
||||
if version == "" {
|
||||
content = `{ "go": {} }`
|
||||
} else {
|
||||
content = `{ "go": { "version": "` + version + `" } }`
|
||||
}
|
||||
_, err := fmt.Fprint(os.Stdout, content)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Failed to write environment json to stdout: ")
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the version of Go to install and output it to stdout as json.
|
||||
func IdentifyEnvironment() {
|
||||
var v versionInfo
|
||||
buildInfo := project.GetBuildInfo(false)
|
||||
goVersionInfo := project.TryReadGoDirective(buildInfo)
|
||||
v.goModVersion, v.goModVersionFound = goVersionInfo.Version, goVersionInfo.Found
|
||||
|
||||
v.goEnvVersionFound = toolchain.IsInstalled()
|
||||
if v.goEnvVersionFound {
|
||||
v.goEnvVersion = toolchain.GetEnvGoVersion()[2:]
|
||||
}
|
||||
|
||||
msg, versionToInstall := getVersionToInstall(v)
|
||||
log.Println(msg)
|
||||
|
||||
outputEnvironmentJson(versionToInstall)
|
||||
}
|
||||
48
go/extractor/autobuilder/build-environment_test.go
Normal file
48
go/extractor/autobuilder/build-environment_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package autobuilder
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetVersionToInstall(t *testing.T) {
|
||||
tests := map[versionInfo]string{
|
||||
// getVersionWhenGoModVersionNotFound()
|
||||
{"", false, "", false}: maxGoVersion,
|
||||
{"", false, "1.2.2", true}: maxGoVersion,
|
||||
{"", false, "9999.0.1", true}: maxGoVersion,
|
||||
{"", false, "1.11.13", true}: "",
|
||||
{"", false, "1.20.3", true}: "",
|
||||
|
||||
// getVersionWhenGoModVersionTooHigh()
|
||||
{"9999.0", true, "", false}: maxGoVersion,
|
||||
{"9999.0", true, "9999.0.1", true}: "",
|
||||
{"9999.0", true, "1.1", true}: maxGoVersion,
|
||||
{"9999.0", true, minGoVersion, false}: maxGoVersion,
|
||||
{"9999.0", true, maxGoVersion, true}: "",
|
||||
|
||||
// getVersionWhenGoModVersionTooLow()
|
||||
{"0.0", true, "", false}: minGoVersion,
|
||||
{"0.0", true, "9999.0", true}: minGoVersion,
|
||||
{"0.0", true, "1.2.2", true}: minGoVersion,
|
||||
{"0.0", true, "1.20.3", true}: "",
|
||||
|
||||
// getVersionWhenGoModVersionSupported()
|
||||
{"1.20", true, "", false}: "1.20",
|
||||
{"1.11", true, "", false}: "1.11",
|
||||
{"1.20", true, "1.2.2", true}: "1.20",
|
||||
{"1.11", true, "1.2.2", true}: "1.11",
|
||||
{"1.20", true, "9999.0.1", true}: "1.20",
|
||||
{"1.11", true, "9999.0.1", true}: "1.11",
|
||||
// go.mod version > go installation version
|
||||
{"1.20", true, "1.11.13", true}: "1.20",
|
||||
{"1.20", true, "1.12", true}: "1.20",
|
||||
// go.mod version <= go installation version (Note comparisons ignore the patch version)
|
||||
{"1.11", true, "1.20", true}: "",
|
||||
{"1.11", true, "1.20.3", true}: "",
|
||||
{"1.20", true, "1.20.3", true}: "",
|
||||
}
|
||||
for input, expected := range tests {
|
||||
_, actual := getVersionToInstall(input)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected getVersionToInstall(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
@@ -10,13 +9,14 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"github.com/github/codeql-go/extractor/autobuilder"
|
||||
"github.com/github/codeql-go/extractor/diagnostics"
|
||||
"github.com/github/codeql-go/extractor/project"
|
||||
"github.com/github/codeql-go/extractor/toolchain"
|
||||
"github.com/github/codeql-go/extractor/util"
|
||||
)
|
||||
|
||||
@@ -56,43 +56,9 @@ Build behavior:
|
||||
fmt.Fprintf(os.Stderr, "Usage:\n\n %s\n", os.Args[0])
|
||||
}
|
||||
|
||||
var goVersion = ""
|
||||
|
||||
// Returns the current Go version as returned by 'go version', e.g. go1.14.4
|
||||
func getEnvGoVersion() string {
|
||||
if goVersion == "" {
|
||||
// Since Go 1.21, running 'go version' in a directory with a 'go.mod' file will attempt to
|
||||
// download the version of Go specified in there. That may either fail or result in us just
|
||||
// being told what's already in 'go.mod'. Setting 'GOTOOLCHAIN' to 'local' will force it
|
||||
// to use the local Go toolchain instead.
|
||||
cmd := exec.Command("go", "version")
|
||||
cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
|
||||
out, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to run the go command, is it installed?\nError: %s", err.Error())
|
||||
}
|
||||
|
||||
goVersion = parseGoVersion(string(out))
|
||||
}
|
||||
return goVersion
|
||||
}
|
||||
|
||||
// The 'go version' command may output warnings on separate lines before
|
||||
// the actual version string is printed. This function parses the output
|
||||
// to retrieve just the version string.
|
||||
func parseGoVersion(data string) string {
|
||||
var lastLine string
|
||||
sc := bufio.NewScanner(strings.NewReader(data))
|
||||
for sc.Scan() {
|
||||
lastLine = sc.Text()
|
||||
}
|
||||
return strings.Fields(lastLine)[2]
|
||||
}
|
||||
|
||||
// Returns the current Go version in semver format, e.g. v1.14.4
|
||||
func getEnvGoSemVer() string {
|
||||
goVersion := getEnvGoVersion()
|
||||
goVersion := toolchain.GetEnvGoVersion()
|
||||
if !strings.HasPrefix(goVersion, "go") {
|
||||
log.Fatalf("Expected 'go version' output of the form 'go1.2.3'; got '%s'", goVersion)
|
||||
}
|
||||
@@ -176,59 +142,6 @@ func restoreRepoLayout(fromDir string, dirEntries []string, scratchDirName strin
|
||||
}
|
||||
}
|
||||
|
||||
// DependencyInstallerMode is an enum describing how dependencies should be installed
|
||||
type DependencyInstallerMode int
|
||||
|
||||
const (
|
||||
// GoGetNoModules represents dependency installation using `go get` without modules
|
||||
GoGetNoModules DependencyInstallerMode = iota
|
||||
// GoGetWithModules represents dependency installation using `go get` with modules
|
||||
GoGetWithModules
|
||||
// Dep represent dependency installation using `dep ensure`
|
||||
Dep
|
||||
// Glide represents dependency installation using `glide install`
|
||||
Glide
|
||||
)
|
||||
|
||||
// ModMode corresponds to the possible values of the -mod flag for the Go compiler
|
||||
type ModMode int
|
||||
|
||||
const (
|
||||
ModUnset ModMode = iota
|
||||
ModReadonly
|
||||
ModMod
|
||||
ModVendor
|
||||
)
|
||||
|
||||
// argsForGoVersion returns the arguments to pass to the Go compiler for the given `ModMode` and
|
||||
// Go version
|
||||
func (m ModMode) argsForGoVersion(version string) []string {
|
||||
switch m {
|
||||
case ModUnset:
|
||||
return []string{}
|
||||
case ModReadonly:
|
||||
return []string{"-mod=readonly"}
|
||||
case ModMod:
|
||||
if !semver.IsValid(version) {
|
||||
log.Fatalf("Invalid Go semver: '%s'", version)
|
||||
}
|
||||
if semver.Compare(version, "v1.14") < 0 {
|
||||
return []string{} // -mod=mod is the default behaviour for go <= 1.13, and is not accepted as an argument
|
||||
} else {
|
||||
return []string{"-mod=mod"}
|
||||
}
|
||||
case ModVendor:
|
||||
return []string{"-mod=vendor"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BuildInfo struct {
|
||||
DepMode DependencyInstallerMode
|
||||
ModMode ModMode
|
||||
BaseDir string
|
||||
}
|
||||
|
||||
// addVersionToMod add a go version directive, e.g. `go 1.14` to a `go.mod` file.
|
||||
func addVersionToMod(version string) bool {
|
||||
cmd := exec.Command("go", "mod", "edit", "-go="+version)
|
||||
@@ -263,169 +176,9 @@ func getSourceDir() string {
|
||||
return srcdir
|
||||
}
|
||||
|
||||
func getDirs(paths []string) []string {
|
||||
dirs := make([]string, len(paths))
|
||||
for i, path := range paths {
|
||||
dirs[i] = filepath.Dir(path)
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
func checkDirsNested(inputDirs []string) (string, bool) {
|
||||
// replace "." with "" so that we can check if all the paths are nested
|
||||
dirs := make([]string, len(inputDirs))
|
||||
for i, inputDir := range inputDirs {
|
||||
if inputDir == "." {
|
||||
dirs[i] = ""
|
||||
} else {
|
||||
dirs[i] = inputDir
|
||||
}
|
||||
}
|
||||
// the paths were generated by a depth-first search so I think they might
|
||||
// be sorted, but we sort them just in case
|
||||
sort.Strings(dirs)
|
||||
for _, dir := range dirs {
|
||||
if !strings.HasPrefix(dir, dirs[0]) {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
return dirs[0], true
|
||||
}
|
||||
|
||||
// Returns the directory to run the go build in and whether to use a go.mod
|
||||
// file.
|
||||
func findGoModFiles(emitDiagnostics bool) (baseDir string, useGoMod bool) {
|
||||
goModPaths := util.FindAllFilesWithName(".", "go.mod", "vendor")
|
||||
if len(goModPaths) == 0 {
|
||||
baseDir = "."
|
||||
useGoMod = false
|
||||
return
|
||||
}
|
||||
goModDirs := getDirs(goModPaths)
|
||||
if util.AnyGoFilesOutsideDirs(".", goModDirs...) {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoFilesOutsideGoModules(goModPaths)
|
||||
}
|
||||
baseDir = "."
|
||||
useGoMod = false
|
||||
return
|
||||
}
|
||||
if len(goModPaths) > 1 {
|
||||
// currently not supported
|
||||
baseDir = "."
|
||||
commonRoot, nested := checkDirsNested(goModDirs)
|
||||
if nested && commonRoot == "" {
|
||||
useGoMod = true
|
||||
} else {
|
||||
useGoMod = false
|
||||
}
|
||||
if emitDiagnostics {
|
||||
if nested {
|
||||
diagnostics.EmitMultipleGoModFoundNested(goModPaths)
|
||||
} else {
|
||||
diagnostics.EmitMultipleGoModFoundNotNested(goModPaths)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if emitDiagnostics {
|
||||
if goModDirs[0] == "." {
|
||||
diagnostics.EmitSingleRootGoModFound(goModPaths[0])
|
||||
} else {
|
||||
diagnostics.EmitSingleNonRootGoModFound(goModPaths[0])
|
||||
}
|
||||
}
|
||||
baseDir = goModDirs[0]
|
||||
useGoMod = true
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the appropriate DependencyInstallerMode for the current project
|
||||
func getDepMode(emitDiagnostics bool) (DependencyInstallerMode, string) {
|
||||
bazelPaths := util.FindAllFilesWithName(".", "BUILD", "vendor")
|
||||
bazelPaths = append(bazelPaths, util.FindAllFilesWithName(".", "BUILD.bazel", "vendor")...)
|
||||
if len(bazelPaths) > 0 {
|
||||
// currently not supported
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitBazelBuildFilesFound(bazelPaths)
|
||||
}
|
||||
}
|
||||
|
||||
goWorkPaths := util.FindAllFilesWithName(".", "go.work", "vendor")
|
||||
if len(goWorkPaths) > 0 {
|
||||
// currently not supported
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoWorkFound(goWorkPaths)
|
||||
}
|
||||
}
|
||||
|
||||
baseDir, useGoMod := findGoModFiles(emitDiagnostics)
|
||||
if useGoMod {
|
||||
log.Println("Found go.mod, enabling go modules")
|
||||
return GoGetWithModules, baseDir
|
||||
}
|
||||
|
||||
if util.FileExists("Gopkg.toml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGopkgTomlFound()
|
||||
}
|
||||
log.Println("Found Gopkg.toml, using dep instead of go get")
|
||||
return Dep, "."
|
||||
}
|
||||
|
||||
if util.FileExists("glide.yaml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGlideYamlFound()
|
||||
}
|
||||
log.Println("Found glide.yaml, using Glide instead of go get")
|
||||
return Glide, "."
|
||||
}
|
||||
return GoGetNoModules, "."
|
||||
}
|
||||
|
||||
type GoVersionInfo struct {
|
||||
// The version string, if any
|
||||
Version string
|
||||
// A value indicating whether a version string was found
|
||||
Found bool
|
||||
}
|
||||
|
||||
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
|
||||
func tryReadGoDirective(buildInfo BuildInfo) GoVersionInfo {
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)$`)
|
||||
goMod, err := os.ReadFile(filepath.Join(buildInfo.BaseDir, "go.mod"))
|
||||
if err != nil {
|
||||
log.Println("Failed to read go.mod to check for missing Go version")
|
||||
} else {
|
||||
matches := versionRe.FindSubmatch(goMod)
|
||||
if matches != nil {
|
||||
if len(matches) > 1 {
|
||||
return GoVersionInfo{string(matches[1]), true}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return GoVersionInfo{"", false}
|
||||
}
|
||||
|
||||
// Returns the appropriate ModMode for the current project
|
||||
func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {
|
||||
if depMode == GoGetWithModules {
|
||||
// if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and
|
||||
// skip the dependency installation step and run the extractor with `-mod=vendor`
|
||||
if util.FileExists(filepath.Join(baseDir, "vendor", "modules.txt")) {
|
||||
return ModVendor
|
||||
} else if util.DirExists(filepath.Join(baseDir, "vendor")) {
|
||||
return ModMod
|
||||
}
|
||||
}
|
||||
return ModUnset
|
||||
}
|
||||
|
||||
// fixGoVendorIssues fixes issues with go vendor for go version >= 1.14
|
||||
func fixGoVendorIssues(buildInfo *BuildInfo, goModVersionFound bool) {
|
||||
if buildInfo.ModMode == ModVendor {
|
||||
func fixGoVendorIssues(buildInfo *project.BuildInfo, goModVersionFound bool) {
|
||||
if buildInfo.ModMode == project.ModVendor {
|
||||
// fix go vendor issues with go versions >= 1.14 when no go version is specified in the go.mod
|
||||
// if this is the case, and dependencies were vendored with an old go version (and therefore
|
||||
// do not contain a '## explicit' annotation, the go command will fail and refuse to do any
|
||||
@@ -433,7 +186,7 @@ func fixGoVendorIssues(buildInfo *BuildInfo, goModVersionFound bool) {
|
||||
//
|
||||
// we work around this by adding an explicit go version of 1.13, which is the last version
|
||||
// where this is not an issue
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
if !goModVersionFound {
|
||||
// if the go.mod does not contain a version line
|
||||
modulesTxt, err := os.ReadFile("vendor/modules.txt")
|
||||
@@ -444,7 +197,7 @@ func fixGoVendorIssues(buildInfo *BuildInfo, goModVersionFound bool) {
|
||||
log.Println("Adding a version directive to the go.mod file as the modules.txt does not have explicit annotations")
|
||||
if !addVersionToMod("1.13") {
|
||||
log.Println("Failed to add a version to the go.mod file to fix explicitly required package bug; not using vendored dependencies")
|
||||
buildInfo.ModMode = ModMod
|
||||
buildInfo.ModMode = project.ModMod
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,9 +206,9 @@ func fixGoVendorIssues(buildInfo *BuildInfo, goModVersionFound bool) {
|
||||
}
|
||||
|
||||
// Determines whether the project needs a GOPATH set up
|
||||
func getNeedGopath(buildInfo BuildInfo, importpath string) bool {
|
||||
func getNeedGopath(buildInfo project.BuildInfo, importpath string) bool {
|
||||
needGopath := true
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
needGopath = false
|
||||
}
|
||||
// if `LGTM_INDEX_NEED_GOPATH` is set, it overrides the value for `needGopath` inferred above
|
||||
@@ -476,9 +229,9 @@ func getNeedGopath(buildInfo BuildInfo, importpath string) bool {
|
||||
}
|
||||
|
||||
// Try to update `go.mod` and `go.sum` if the go version is >= 1.16.
|
||||
func tryUpdateGoModAndGoSum(buildInfo BuildInfo) {
|
||||
func tryUpdateGoModAndGoSum(buildInfo project.BuildInfo) {
|
||||
// Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here:
|
||||
if buildInfo.ModMode != ModVendor && buildInfo.DepMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 {
|
||||
if buildInfo.ModMode != project.ModVendor && buildInfo.DepMode == project.GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 {
|
||||
// stat go.mod and go.sum
|
||||
goModPath := filepath.Join(buildInfo.BaseDir, "go.mod")
|
||||
beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath)
|
||||
@@ -642,7 +395,7 @@ func setGopath(root string) {
|
||||
|
||||
// Try to build the project without custom commands. If that fails, return a boolean indicating
|
||||
// that we should install dependencies ourselves.
|
||||
func buildWithoutCustomCommands(modMode ModMode) bool {
|
||||
func buildWithoutCustomCommands(modMode project.ModMode) bool {
|
||||
shouldInstallDependencies := false
|
||||
// try to build the project
|
||||
buildSucceeded := autobuilder.Autobuild()
|
||||
@@ -653,7 +406,7 @@ func buildWithoutCustomCommands(modMode ModMode) bool {
|
||||
log.Println("Build failed, continuing to install dependencies.")
|
||||
|
||||
shouldInstallDependencies = true
|
||||
} else if util.DepErrors("./...", modMode.argsForGoVersion(getEnvGoSemVer())...) {
|
||||
} else if util.DepErrors("./...", modMode.ArgsForGoVersion(getEnvGoSemVer())...) {
|
||||
log.Println("Dependencies are still not resolving after the build, continuing to install dependencies.")
|
||||
|
||||
shouldInstallDependencies = true
|
||||
@@ -696,10 +449,10 @@ func buildWithCustomCommands(inst string) {
|
||||
}
|
||||
|
||||
// Install dependencies using the given dependency installer mode.
|
||||
func installDependencies(buildInfo BuildInfo) {
|
||||
func installDependencies(buildInfo project.BuildInfo) {
|
||||
// automatically determine command to install dependencies
|
||||
var install *exec.Cmd
|
||||
if buildInfo.DepMode == Dep {
|
||||
if buildInfo.DepMode == project.Dep {
|
||||
// set up the dep cache if SEMMLE_CACHE is set
|
||||
cacheDir := os.Getenv("SEMMLE_CACHE")
|
||||
if cacheDir != "" {
|
||||
@@ -729,14 +482,14 @@ func installDependencies(buildInfo BuildInfo) {
|
||||
install = exec.Command("dep", "ensure", "-v")
|
||||
}
|
||||
log.Println("Installing dependencies using `dep ensure`.")
|
||||
} else if buildInfo.DepMode == Glide {
|
||||
} else if buildInfo.DepMode == project.Glide {
|
||||
install = exec.Command("glide", "install")
|
||||
log.Println("Installing dependencies using `glide install`")
|
||||
} else {
|
||||
// explicitly set go module support
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
} else if buildInfo.DepMode == GoGetNoModules {
|
||||
} else if buildInfo.DepMode == project.GoGetNoModules {
|
||||
os.Setenv("GO111MODULE", "off")
|
||||
}
|
||||
|
||||
@@ -749,15 +502,15 @@ func installDependencies(buildInfo BuildInfo) {
|
||||
}
|
||||
|
||||
// Run the extractor.
|
||||
func extract(buildInfo BuildInfo) {
|
||||
func extract(buildInfo project.BuildInfo) {
|
||||
extractor, err := util.GetExtractorPath()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not determine path of extractor: %v.\n", err)
|
||||
}
|
||||
|
||||
extractorArgs := []string{}
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
extractorArgs = append(extractorArgs, buildInfo.ModMode.argsForGoVersion(getEnvGoSemVer())...)
|
||||
if buildInfo.DepMode == project.GoGetWithModules {
|
||||
extractorArgs = append(extractorArgs, buildInfo.ModMode.ArgsForGoVersion(getEnvGoSemVer())...)
|
||||
}
|
||||
extractorArgs = append(extractorArgs, "./...")
|
||||
|
||||
@@ -772,15 +525,9 @@ func extract(buildInfo BuildInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
func getBuildInfo(emitDiagnostics bool) BuildInfo {
|
||||
depMode, baseDir := getDepMode(true)
|
||||
modMode := getModMode(depMode, baseDir)
|
||||
return BuildInfo{depMode, modMode, baseDir}
|
||||
}
|
||||
|
||||
// Build the project and run the extractor.
|
||||
func installDependenciesAndBuild() {
|
||||
log.Printf("Autobuilder was built with %s, environment has %s\n", runtime.Version(), getEnvGoVersion())
|
||||
log.Printf("Autobuilder was built with %s, environment has %s\n", runtime.Version(), toolchain.GetEnvGoVersion())
|
||||
|
||||
srcdir := getSourceDir()
|
||||
|
||||
@@ -789,17 +536,20 @@ func installDependenciesAndBuild() {
|
||||
|
||||
// determine how to install dependencies and whether a GOPATH needs to be set up before
|
||||
// extraction
|
||||
buildInfo := getBuildInfo(true)
|
||||
buildInfo := project.GetBuildInfo(true)
|
||||
if _, present := os.LookupEnv("GO111MODULE"); !present {
|
||||
os.Setenv("GO111MODULE", "auto")
|
||||
}
|
||||
|
||||
goVersionInfo := tryReadGoDirective(buildInfo)
|
||||
goVersionInfo := project.TryReadGoDirective(buildInfo)
|
||||
|
||||
// This diagnostic is not required if the system Go version is 1.21 or greater, since the
|
||||
// Go tooling should install required Go versions as needed.
|
||||
if semver.Compare(getEnvGoSemVer(), "v1.21.0") < 0 && goVersionInfo.Found && semver.Compare("v"+goVersionInfo.Version, getEnvGoSemVer()) > 0 {
|
||||
diagnostics.EmitNewerGoVersionNeeded()
|
||||
if val, _ := os.LookupEnv("GITHUB_ACTIONS"); val == "true" {
|
||||
log.Printf("The go.mod version is newer than the installed version of Go. Consider adding an actions/setup-go step to your workflow.\n")
|
||||
}
|
||||
}
|
||||
|
||||
fixGoVendorIssues(&buildInfo, goVersionInfo.Found)
|
||||
@@ -833,18 +583,18 @@ func installDependenciesAndBuild() {
|
||||
buildWithCustomCommands(inst)
|
||||
}
|
||||
|
||||
if buildInfo.ModMode == ModVendor {
|
||||
if buildInfo.ModMode == project.ModVendor {
|
||||
// test if running `go` with -mod=vendor works, and if it doesn't, try to fallback to -mod=mod
|
||||
// or not set if the go version < 1.14. Note we check this post-build in case the build brings
|
||||
// the vendor directory up to date.
|
||||
if !checkVendor() {
|
||||
buildInfo.ModMode = ModMod
|
||||
buildInfo.ModMode = project.ModMod
|
||||
log.Println("The vendor directory is not consistent with the go.mod; not using vendored dependencies.")
|
||||
}
|
||||
}
|
||||
|
||||
if shouldInstallDependencies {
|
||||
if buildInfo.ModMode == ModVendor {
|
||||
if buildInfo.ModMode == project.ModVendor {
|
||||
log.Printf("Skipping dependency installation because a Go vendor directory was found.")
|
||||
} else {
|
||||
installDependencies(buildInfo)
|
||||
@@ -854,288 +604,11 @@ func installDependenciesAndBuild() {
|
||||
extract(buildInfo)
|
||||
}
|
||||
|
||||
const minGoVersion = "1.11"
|
||||
const maxGoVersion = "1.21"
|
||||
|
||||
// Check if `version` is lower than `minGoVersion`. Note that for this comparison we ignore the
|
||||
// patch part of the version, so 1.20.1 and 1.20 are considered equal.
|
||||
func belowSupportedRange(version string) bool {
|
||||
return semver.Compare(semver.MajorMinor("v"+version), "v"+minGoVersion) < 0
|
||||
}
|
||||
|
||||
// Check if `version` is higher than `maxGoVersion`. Note that for this comparison we ignore the
|
||||
// patch part of the version, so 1.20.1 and 1.20 are considered equal.
|
||||
func aboveSupportedRange(version string) bool {
|
||||
return semver.Compare(semver.MajorMinor("v"+version), "v"+maxGoVersion) > 0
|
||||
}
|
||||
|
||||
// Check if `version` is lower than `minGoVersion` or higher than `maxGoVersion`. Note that for
|
||||
// this comparison we ignore the patch part of the version, so 1.20.1 and 1.20 are considered
|
||||
// equal.
|
||||
func outsideSupportedRange(version string) bool {
|
||||
return belowSupportedRange(version) || aboveSupportedRange(version)
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersionFound` is false, emit a diagnostic and return the version to install,
|
||||
// or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionNotFound(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// There is no Go version installed in the environment. We have no indication which version
|
||||
// was intended to be used to build this project. Go versions are generally backwards
|
||||
// compatible, so we install the maximum supported version.
|
||||
msg = "No version of Go installed and no `go.mod` file found. Requesting the maximum " +
|
||||
"supported version of Go (" + maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitNoGoModAndNoGoEnv(msg)
|
||||
} else if outsideSupportedRange(v.goEnvVersion) {
|
||||
// The Go version installed in the environment is not supported. We have no indication
|
||||
// which version was intended to be used to build this project. Go versions are generally
|
||||
// backwards compatible, so we install the maximum supported version.
|
||||
msg = "No `go.mod` file found. The version of Go installed in the environment (" +
|
||||
v.goEnvVersion + ") is outside of the supported range (" + minGoVersion + "-" +
|
||||
maxGoVersion + "). Requesting the maximum supported version of Go (" + maxGoVersion +
|
||||
")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitNoGoModAndGoEnvUnsupported(msg)
|
||||
} else {
|
||||
// The version of Go that is installed is supported. We have no indication which version
|
||||
// was intended to be used to build this project. We assume that the installed version is
|
||||
// suitable and do not install a version of Go.
|
||||
msg = "No `go.mod` file found. Version " + v.goEnvVersion + " installed in the " +
|
||||
"environment is supported. Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitNoGoModAndGoEnvSupported(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersion` is above the supported range, emit a diagnostic and return the
|
||||
// version to install, or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionTooHigh(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// The version in the `go.mod` file is above the supported range. There is no Go version
|
||||
// installed. We install the maximum supported version as a best effort.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). No version of Go installed. Requesting the maximum supported version of Go (" +
|
||||
maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitGoModVersionTooHighAndNoGoEnv(msg)
|
||||
} else if aboveSupportedRange(v.goEnvVersion) {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is above the supported range. We do not install a version of Go.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionTooHigh(msg)
|
||||
} else if belowSupportedRange(v.goEnvVersion) {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is below the supported range. We install the maximum supported version as
|
||||
// a best effort.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). Requesting the maximum supported version of Go (" + maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionTooLow(msg)
|
||||
} else if semver.Compare("v"+maxGoVersion, "v"+v.goEnvVersion) > 0 {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is supported and below the maximum supported version. We install the
|
||||
// maximum supported version as a best effort.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is below the maximum supported version (" + maxGoVersion +
|
||||
"). Requesting the maximum supported version of Go (" + maxGoVersion + ")."
|
||||
version = maxGoVersion
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionBelowMax(msg)
|
||||
} else {
|
||||
// The version in the `go.mod` file is above the supported range. The version of Go that
|
||||
// is installed is the maximum supported version. We do not install a version of Go.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is above the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is the maximum supported version (" + maxGoVersion +
|
||||
"). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionTooHighAndEnvVersionMax(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersion` is below the supported range, emit a diagnostic and return the
|
||||
// version to install, or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionTooLow(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// There is no Go version installed. The version in the `go.mod` file is below the
|
||||
// supported range. Go versions are generally backwards compatible, so we install the
|
||||
// minimum supported version.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). No version of Go installed. Requesting the minimum supported version of Go (" +
|
||||
minGoVersion + ")."
|
||||
version = minGoVersion
|
||||
diagnostics.EmitGoModVersionTooLowAndNoGoEnv(msg)
|
||||
} else if outsideSupportedRange(v.goEnvVersion) {
|
||||
// The version of Go that is installed is outside of the supported range. The version
|
||||
// in the `go.mod` file is below the supported range. Go versions are generally
|
||||
// backwards compatible, so we install the minimum supported version.
|
||||
msg = "The version of Go found in the `go.mod` file (" + v.goModVersion +
|
||||
") is below the supported range (" + minGoVersion + "-" + maxGoVersion +
|
||||
"). The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + "). " +
|
||||
"Requesting the minimum supported version of Go (" + minGoVersion + ")."
|
||||
version = minGoVersion
|
||||
diagnostics.EmitGoModVersionTooLowAndEnvVersionUnsupported(msg)
|
||||
} else {
|
||||
// The version of Go that is installed is supported. The version in the `go.mod` file is
|
||||
// below the supported range. We do not install a version of Go.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is supported and is high enough for the version found in the `go.mod` file (" +
|
||||
v.goModVersion + "). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionTooLowAndEnvVersionSupported(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Assuming `v.goModVersion` is in the supported range, emit a diagnostic and return the version
|
||||
// to install, or the empty string if we should not attempt to install a version of Go.
|
||||
func getVersionWhenGoModVersionSupported(v versionInfo) (msg, version string) {
|
||||
if !v.goEnvVersionFound {
|
||||
// There is no Go version installed. The version in the `go.mod` file is supported.
|
||||
// We install the version from the `go.mod` file.
|
||||
msg = "No version of Go installed. Requesting the version of Go found in the `go.mod` " +
|
||||
"file (" + v.goModVersion + ")."
|
||||
version = v.goModVersion
|
||||
diagnostics.EmitGoModVersionSupportedAndNoGoEnv(msg)
|
||||
} else if outsideSupportedRange(v.goEnvVersion) {
|
||||
// The version of Go that is installed is outside of the supported range. The version in
|
||||
// the `go.mod` file is supported. We install the version from the `go.mod` file.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is outside of the supported range (" + minGoVersion + "-" + maxGoVersion + "). " +
|
||||
"Requesting the version of Go from the `go.mod` file (" +
|
||||
v.goModVersion + ")."
|
||||
version = v.goModVersion
|
||||
diagnostics.EmitGoModVersionSupportedAndGoEnvUnsupported(msg)
|
||||
} else if semver.Compare("v"+v.goModVersion, "v"+v.goEnvVersion) > 0 {
|
||||
// The version of Go that is installed is supported. The version in the `go.mod` file is
|
||||
// supported and is higher than the version that is installed. We install the version from
|
||||
// the `go.mod` file.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is lower than the version found in the `go.mod` file (" + v.goModVersion +
|
||||
"). Requesting the version of Go from the `go.mod` file (" + v.goModVersion + ")."
|
||||
version = v.goModVersion
|
||||
diagnostics.EmitGoModVersionSupportedHigherGoEnv(msg)
|
||||
} else {
|
||||
// The version of Go that is installed is supported. The version in the `go.mod` file is
|
||||
// supported and is lower than or equal to the version that is installed. We do not install
|
||||
// a version of Go.
|
||||
msg = "The version of Go installed in the environment (" + v.goEnvVersion +
|
||||
") is supported and is high enough for the version found in the `go.mod` file (" +
|
||||
v.goModVersion + "). Not requesting any version of Go."
|
||||
version = ""
|
||||
diagnostics.EmitGoModVersionSupportedLowerEqualGoEnv(msg)
|
||||
}
|
||||
|
||||
return msg, version
|
||||
}
|
||||
|
||||
// Check the versions of Go found in the environment and in the `go.mod` file, and return a
|
||||
// version to install. If the version is the empty string then no installation is required.
|
||||
// We never return a version of Go that is outside of the supported range.
|
||||
//
|
||||
// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+
|
||||
// | Found in go.mod > | *None* | *Below min supported* | *In supported range* | *Above max supported |
|
||||
// | Installed \/ | | | | |
|
||||
// |-----------------------|-----------------------|-----------------------|-----------------------------------------------------|------------------------------------------------|
|
||||
// | *None* | Install max supported | Install min supported | Install version from go.mod | Install max supported |
|
||||
// | *Below min supported* | Install max supported | Install min supported | Install version from go.mod | Install max supported |
|
||||
// | *In supported range* | No action | No action | Install version from go.mod if newer than installed | Install max supported if newer than installed |
|
||||
// | *Above max supported* | Install max supported | Install min supported | Install version from go.mod | No action |
|
||||
// +-----------------------+-----------------------+-----------------------+-----------------------------------------------------+------------------------------------------------+
|
||||
func getVersionToInstall(v versionInfo) (msg, version string) {
|
||||
if !v.goModVersionFound {
|
||||
return getVersionWhenGoModVersionNotFound(v)
|
||||
}
|
||||
|
||||
if aboveSupportedRange(v.goModVersion) {
|
||||
return getVersionWhenGoModVersionTooHigh(v)
|
||||
}
|
||||
|
||||
if belowSupportedRange(v.goModVersion) {
|
||||
return getVersionWhenGoModVersionTooLow(v)
|
||||
}
|
||||
|
||||
return getVersionWhenGoModVersionSupported(v)
|
||||
}
|
||||
|
||||
// Output some JSON to stdout specifying the version of Go to install, unless `version` is the
|
||||
// empty string.
|
||||
func outputEnvironmentJson(version string) {
|
||||
var content string
|
||||
if version == "" {
|
||||
content = `{ "go": {} }`
|
||||
} else {
|
||||
content = `{ "go": { "version": "` + version + `" } }`
|
||||
}
|
||||
_, err := fmt.Fprint(os.Stdout, content)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Failed to write environment json to stdout: ")
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
type versionInfo struct {
|
||||
goModVersion string // The version of Go found in the go directive in the `go.mod` file.
|
||||
goModVersionFound bool // Whether a `go` directive was found in the `go.mod` file.
|
||||
goEnvVersion string // The version of Go found in the environment.
|
||||
goEnvVersionFound bool // Whether an installation of Go was found in the environment.
|
||||
}
|
||||
|
||||
func (v versionInfo) String() string {
|
||||
return fmt.Sprintf(
|
||||
"go.mod version: %s, go.mod directive found: %t, go env version: %s, go installation found: %t",
|
||||
v.goModVersion, v.goModVersionFound, v.goEnvVersion, v.goEnvVersionFound)
|
||||
}
|
||||
|
||||
// Check if Go is installed in the environment.
|
||||
func isGoInstalled() bool {
|
||||
_, err := exec.LookPath("go")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Get the version of Go to install and output it to stdout as json.
|
||||
func identifyEnvironment() {
|
||||
var v versionInfo
|
||||
buildInfo := getBuildInfo(false)
|
||||
goVersionInfo := tryReadGoDirective(buildInfo)
|
||||
v.goModVersion, v.goModVersionFound = goVersionInfo.Version, goVersionInfo.Found
|
||||
|
||||
v.goEnvVersionFound = isGoInstalled()
|
||||
if v.goEnvVersionFound {
|
||||
v.goEnvVersion = getEnvGoVersion()[2:]
|
||||
}
|
||||
|
||||
msg, versionToInstall := getVersionToInstall(v)
|
||||
log.Println(msg)
|
||||
|
||||
outputEnvironmentJson(versionToInstall)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 1 {
|
||||
installDependenciesAndBuild()
|
||||
} else if len(os.Args) == 2 && os.Args[1] == "--identify-environment" {
|
||||
identifyEnvironment()
|
||||
autobuilder.IdentifyEnvironment()
|
||||
} else {
|
||||
usage()
|
||||
os.Exit(2)
|
||||
|
||||
@@ -20,61 +20,3 @@ func TestGetImportPathFromRepoURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGoVersion(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"go version go1.18.9 linux/amd64": "go1.18.9",
|
||||
"warning: GOPATH set to GOROOT (/usr/local/go) has no effect\ngo version go1.18.9 linux/amd64": "go1.18.9",
|
||||
}
|
||||
for input, expected := range tests {
|
||||
actual := parseGoVersion(input)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected parseGoVersion(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersionToInstall(t *testing.T) {
|
||||
tests := map[versionInfo]string{
|
||||
// getVersionWhenGoModVersionNotFound()
|
||||
{"", false, "", false}: maxGoVersion,
|
||||
{"", false, "1.2.2", true}: maxGoVersion,
|
||||
{"", false, "9999.0.1", true}: maxGoVersion,
|
||||
{"", false, "1.11.13", true}: "",
|
||||
{"", false, "1.20.3", true}: "",
|
||||
|
||||
// getVersionWhenGoModVersionTooHigh()
|
||||
{"9999.0", true, "", false}: maxGoVersion,
|
||||
{"9999.0", true, "9999.0.1", true}: "",
|
||||
{"9999.0", true, "1.1", true}: maxGoVersion,
|
||||
{"9999.0", true, minGoVersion, false}: maxGoVersion,
|
||||
{"9999.0", true, maxGoVersion, true}: "",
|
||||
|
||||
// getVersionWhenGoModVersionTooLow()
|
||||
{"0.0", true, "", false}: minGoVersion,
|
||||
{"0.0", true, "9999.0", true}: minGoVersion,
|
||||
{"0.0", true, "1.2.2", true}: minGoVersion,
|
||||
{"0.0", true, "1.20.3", true}: "",
|
||||
|
||||
// getVersionWhenGoModVersionSupported()
|
||||
{"1.20", true, "", false}: "1.20",
|
||||
{"1.11", true, "", false}: "1.11",
|
||||
{"1.20", true, "1.2.2", true}: "1.20",
|
||||
{"1.11", true, "1.2.2", true}: "1.11",
|
||||
{"1.20", true, "9999.0.1", true}: "1.20",
|
||||
{"1.11", true, "9999.0.1", true}: "1.11",
|
||||
// go.mod version > go installation version
|
||||
{"1.20", true, "1.11.13", true}: "1.20",
|
||||
{"1.20", true, "1.12", true}: "1.20",
|
||||
// go.mod version <= go installation version (Note comparisons ignore the patch version)
|
||||
{"1.11", true, "1.20", true}: "",
|
||||
{"1.11", true, "1.20.3", true}: "",
|
||||
{"1.20", true, "1.20.3", true}: "",
|
||||
}
|
||||
for input, expected := range tests {
|
||||
_, actual := getVersionToInstall(input)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected getVersionToInstall(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
233
go/extractor/project/project.go
Normal file
233
go/extractor/project/project.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/github/codeql-go/extractor/diagnostics"
|
||||
"github.com/github/codeql-go/extractor/util"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
func getDirs(paths []string) []string {
|
||||
dirs := make([]string, len(paths))
|
||||
for i, path := range paths {
|
||||
dirs[i] = filepath.Dir(path)
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
func checkDirsNested(inputDirs []string) (string, bool) {
|
||||
// replace "." with "" so that we can check if all the paths are nested
|
||||
dirs := make([]string, len(inputDirs))
|
||||
for i, inputDir := range inputDirs {
|
||||
if inputDir == "." {
|
||||
dirs[i] = ""
|
||||
} else {
|
||||
dirs[i] = inputDir
|
||||
}
|
||||
}
|
||||
// the paths were generated by a depth-first search so I think they might
|
||||
// be sorted, but we sort them just in case
|
||||
sort.Strings(dirs)
|
||||
for _, dir := range dirs {
|
||||
if !strings.HasPrefix(dir, dirs[0]) {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
return dirs[0], true
|
||||
}
|
||||
|
||||
// Returns the directory to run the go build in and whether to use a go.mod
|
||||
// file.
|
||||
func findGoModFiles(emitDiagnostics bool) (baseDir string, useGoMod bool) {
|
||||
goModPaths := util.FindAllFilesWithName(".", "go.mod", "vendor")
|
||||
if len(goModPaths) == 0 {
|
||||
baseDir = "."
|
||||
useGoMod = false
|
||||
return
|
||||
}
|
||||
goModDirs := getDirs(goModPaths)
|
||||
if util.AnyGoFilesOutsideDirs(".", goModDirs...) {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoFilesOutsideGoModules(goModPaths)
|
||||
}
|
||||
baseDir = "."
|
||||
useGoMod = false
|
||||
return
|
||||
}
|
||||
if len(goModPaths) > 1 {
|
||||
// currently not supported
|
||||
baseDir = "."
|
||||
commonRoot, nested := checkDirsNested(goModDirs)
|
||||
if nested && commonRoot == "" {
|
||||
useGoMod = true
|
||||
} else {
|
||||
useGoMod = false
|
||||
}
|
||||
if emitDiagnostics {
|
||||
if nested {
|
||||
diagnostics.EmitMultipleGoModFoundNested(goModPaths)
|
||||
} else {
|
||||
diagnostics.EmitMultipleGoModFoundNotNested(goModPaths)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if emitDiagnostics {
|
||||
if goModDirs[0] == "." {
|
||||
diagnostics.EmitSingleRootGoModFound(goModPaths[0])
|
||||
} else {
|
||||
diagnostics.EmitSingleNonRootGoModFound(goModPaths[0])
|
||||
}
|
||||
}
|
||||
baseDir = goModDirs[0]
|
||||
useGoMod = true
|
||||
return
|
||||
}
|
||||
|
||||
// DependencyInstallerMode is an enum describing how dependencies should be installed
|
||||
type DependencyInstallerMode int
|
||||
|
||||
const (
|
||||
// GoGetNoModules represents dependency installation using `go get` without modules
|
||||
GoGetNoModules DependencyInstallerMode = iota
|
||||
// GoGetWithModules represents dependency installation using `go get` with modules
|
||||
GoGetWithModules
|
||||
// Dep represent dependency installation using `dep ensure`
|
||||
Dep
|
||||
// Glide represents dependency installation using `glide install`
|
||||
Glide
|
||||
)
|
||||
|
||||
// Returns the appropriate DependencyInstallerMode for the current project
|
||||
func getDepMode(emitDiagnostics bool) (DependencyInstallerMode, string) {
|
||||
bazelPaths := util.FindAllFilesWithName(".", "BUILD", "vendor")
|
||||
bazelPaths = append(bazelPaths, util.FindAllFilesWithName(".", "BUILD.bazel", "vendor")...)
|
||||
if len(bazelPaths) > 0 {
|
||||
// currently not supported
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitBazelBuildFilesFound(bazelPaths)
|
||||
}
|
||||
}
|
||||
|
||||
goWorkPaths := util.FindAllFilesWithName(".", "go.work", "vendor")
|
||||
if len(goWorkPaths) > 0 {
|
||||
// currently not supported
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGoWorkFound(goWorkPaths)
|
||||
}
|
||||
}
|
||||
|
||||
baseDir, useGoMod := findGoModFiles(emitDiagnostics)
|
||||
if useGoMod {
|
||||
log.Println("Found go.mod, enabling go modules")
|
||||
return GoGetWithModules, baseDir
|
||||
}
|
||||
|
||||
if util.FileExists("Gopkg.toml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGopkgTomlFound()
|
||||
}
|
||||
log.Println("Found Gopkg.toml, using dep instead of go get")
|
||||
return Dep, "."
|
||||
}
|
||||
|
||||
if util.FileExists("glide.yaml") {
|
||||
if emitDiagnostics {
|
||||
diagnostics.EmitGlideYamlFound()
|
||||
}
|
||||
log.Println("Found glide.yaml, using Glide instead of go get")
|
||||
return Glide, "."
|
||||
}
|
||||
return GoGetNoModules, "."
|
||||
}
|
||||
|
||||
// ModMode corresponds to the possible values of the -mod flag for the Go compiler
|
||||
type ModMode int
|
||||
|
||||
const (
|
||||
ModUnset ModMode = iota
|
||||
ModReadonly
|
||||
ModMod
|
||||
ModVendor
|
||||
)
|
||||
|
||||
// argsForGoVersion returns the arguments to pass to the Go compiler for the given `ModMode` and
|
||||
// Go version
|
||||
func (m ModMode) ArgsForGoVersion(version string) []string {
|
||||
switch m {
|
||||
case ModUnset:
|
||||
return []string{}
|
||||
case ModReadonly:
|
||||
return []string{"-mod=readonly"}
|
||||
case ModMod:
|
||||
if !semver.IsValid(version) {
|
||||
log.Fatalf("Invalid Go semver: '%s'", version)
|
||||
}
|
||||
if semver.Compare(version, "v1.14") < 0 {
|
||||
return []string{} // -mod=mod is the default behaviour for go <= 1.13, and is not accepted as an argument
|
||||
} else {
|
||||
return []string{"-mod=mod"}
|
||||
}
|
||||
case ModVendor:
|
||||
return []string{"-mod=vendor"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the appropriate ModMode for the current project
|
||||
func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode {
|
||||
if depMode == GoGetWithModules {
|
||||
// if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and
|
||||
// skip the dependency installation step and run the extractor with `-mod=vendor`
|
||||
if util.FileExists(filepath.Join(baseDir, "vendor", "modules.txt")) {
|
||||
return ModVendor
|
||||
} else if util.DirExists(filepath.Join(baseDir, "vendor")) {
|
||||
return ModMod
|
||||
}
|
||||
}
|
||||
return ModUnset
|
||||
}
|
||||
|
||||
type BuildInfo struct {
|
||||
DepMode DependencyInstallerMode
|
||||
ModMode ModMode
|
||||
BaseDir string
|
||||
}
|
||||
|
||||
func GetBuildInfo(emitDiagnostics bool) BuildInfo {
|
||||
depMode, baseDir := getDepMode(true)
|
||||
modMode := getModMode(depMode, baseDir)
|
||||
return BuildInfo{depMode, modMode, baseDir}
|
||||
}
|
||||
|
||||
type GoVersionInfo struct {
|
||||
// The version string, if any
|
||||
Version string
|
||||
// A value indicating whether a version string was found
|
||||
Found bool
|
||||
}
|
||||
|
||||
// Tries to open `go.mod` and read a go directive, returning the version and whether it was found.
|
||||
func TryReadGoDirective(buildInfo BuildInfo) GoVersionInfo {
|
||||
if buildInfo.DepMode == GoGetWithModules {
|
||||
versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+(\.[0-9]+)?)$`)
|
||||
goMod, err := os.ReadFile(filepath.Join(buildInfo.BaseDir, "go.mod"))
|
||||
if err != nil {
|
||||
log.Println("Failed to read go.mod to check for missing Go version")
|
||||
} else {
|
||||
matches := versionRe.FindSubmatch(goMod)
|
||||
if matches != nil {
|
||||
if len(matches) > 1 {
|
||||
return GoVersionInfo{string(matches[1]), true}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return GoVersionInfo{"", false}
|
||||
}
|
||||
49
go/extractor/toolchain/toolchain.go
Normal file
49
go/extractor/toolchain/toolchain.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package toolchain
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Check if Go is installed in the environment.
|
||||
func IsInstalled() bool {
|
||||
_, err := exec.LookPath("go")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
var goVersion = ""
|
||||
|
||||
// Returns the current Go version as returned by 'go version', e.g. go1.14.4
|
||||
func GetEnvGoVersion() string {
|
||||
if goVersion == "" {
|
||||
// Since Go 1.21, running 'go version' in a directory with a 'go.mod' file will attempt to
|
||||
// download the version of Go specified in there. That may either fail or result in us just
|
||||
// being told what's already in 'go.mod'. Setting 'GOTOOLCHAIN' to 'local' will force it
|
||||
// to use the local Go toolchain instead.
|
||||
cmd := exec.Command("go", "version")
|
||||
cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
|
||||
out, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to run the go command, is it installed?\nError: %s", err.Error())
|
||||
}
|
||||
|
||||
goVersion = parseGoVersion(string(out))
|
||||
}
|
||||
return goVersion
|
||||
}
|
||||
|
||||
// The 'go version' command may output warnings on separate lines before
|
||||
// the actual version string is printed. This function parses the output
|
||||
// to retrieve just the version string.
|
||||
func parseGoVersion(data string) string {
|
||||
var lastLine string
|
||||
sc := bufio.NewScanner(strings.NewReader(data))
|
||||
for sc.Scan() {
|
||||
lastLine = sc.Text()
|
||||
}
|
||||
return strings.Fields(lastLine)[2]
|
||||
}
|
||||
16
go/extractor/toolchain/toolchain_test.go
Normal file
16
go/extractor/toolchain/toolchain_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package toolchain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseGoVersion(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"go version go1.18.9 linux/amd64": "go1.18.9",
|
||||
"warning: GOPATH set to GOROOT (/usr/local/go) has no effect\ngo version go1.18.9 linux/amd64": "go1.18.9",
|
||||
}
|
||||
for input, expected := range tests {
|
||||
actual := parseGoVersion(input)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected parseGoVersion(\"%s\") to be \"%s\", but got \"%s\".", input, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted many deprecated predicates and classes with uppercase `TLD`, `HTTP`, `SQL`, `URL` etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated and unused `Source` class from the `SharedXss` module of `Xss.qll`
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user