diff --git a/.gitignore b/.gitignore index 0ad853f..4f99d17 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,9 @@ node_modules # Generated via `codeql-mcp-server-ci` actions workflow models-output.json -# Test databases created via `codeql test run` +# Test databases and artifacts created via `codeql test run` *.testproj +*.actual +*.bqrs trap diff --git a/languages/go/custom/src/UncontrolledAllocationSize/UncontrolledAllocationSize.qhelp b/languages/go/custom/src/UncontrolledAllocationSize/UncontrolledAllocationSize.qhelp new file mode 100644 index 0000000..64e28a9 --- /dev/null +++ b/languages/go/custom/src/UncontrolledAllocationSize/UncontrolledAllocationSize.qhelp @@ -0,0 +1,50 @@ + + + +

+Allocating memory with a size controlled by an external user can result in integer overflow or +excessive memory consumption, leading to denial of service (DoS) attacks. +

+ +
+ + +

+Ensure that allocation sizes are properly validated and restricted to reasonable limits before +allocating memory. Consider using a maximum size constant or validating the size against known +safe bounds. +

+ +
+ + +

+In the following example, the allocation size is directly controlled by user input without +validation: +

+ + + +

+In the corrected example, the allocation size is validated against a maximum value before +allocating: +

+ + + +
+ + +
  • + OWASP: + Denial of Service. +
  • +
  • + CWE: + CWE-770: Allocation of Resources Without Limits or Throttling. +
  • + +
    +
    diff --git a/languages/go/custom/src/UncontrolledAllocationSize/UncontrolledAllocationSize.ql b/languages/go/custom/src/UncontrolledAllocationSize/UncontrolledAllocationSize.ql new file mode 100644 index 0000000..78be9fa --- /dev/null +++ b/languages/go/custom/src/UncontrolledAllocationSize/UncontrolledAllocationSize.ql @@ -0,0 +1,39 @@ +/** + * @name Uncontrolled allocation size + * @description Allocating memory with a size controlled by an external user can result in integer + * overflow or denial of service (DoS). + * @kind path-problem + * @problem.severity warning + * @security-severity 7.5 + * @precision high + * @id go/uncontrolled-allocation-size + * @tags security + * external/cwe/cwe-770 + */ + +import go +import semmle.go.dataflow.DataFlow +import semmle.go.dataflow.TaintTracking + +/** + * A data flow configuration for tracking user-controlled values that flow to allocation size arguments. + */ +module UncontrolledAllocationSizeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + predicate isSink(DataFlow::Node sink) { + exists(CallExpr call | + call.getTarget().getName() = "make" and + sink.asExpr() = call.getArgument(1) + ) + } +} + +module UncontrolledAllocationSizeFlow = TaintTracking::Global; + +import UncontrolledAllocationSizeFlow::PathGraph + +from UncontrolledAllocationSizeFlow::PathNode source, UncontrolledAllocationSizeFlow::PathNode sink +where UncontrolledAllocationSizeFlow::flowPath(source, sink) +select sink.getNode(), source, sink, "This memory allocation depends on a $@.", + source.getNode(), "user-provided value" diff --git a/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSize.expected b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSize.expected new file mode 100644 index 0000000..434a649 --- /dev/null +++ b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSize.expected @@ -0,0 +1,18 @@ +edges +| UncontrolledAllocationSizeBad.go:11:12:11:16 | selection of URL | UncontrolledAllocationSizeBad.go:11:12:11:24 | call to Query | provenance | Src:MaD:1 MaD:2 | +| UncontrolledAllocationSizeBad.go:11:12:11:24 | call to Query | UncontrolledAllocationSizeBad.go:13:15:13:20 | source | provenance | | +| UncontrolledAllocationSizeBad.go:13:15:13:20 | source | UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | provenance | MaD:3 | +| UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | provenance | | +| UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | provenance | | +| UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | provenance | Config | +nodes +| UncontrolledAllocationSizeBad.go:11:12:11:16 | selection of URL | semmle.label | selection of URL | +| UncontrolledAllocationSizeBad.go:11:12:11:24 | call to Query | semmle.label | call to Query | +| UncontrolledAllocationSizeBad.go:13:15:13:20 | source | semmle.label | source | +| UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | semmle.label | call to Get | +| UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | semmle.label | ... := ...[0] | +| UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | semmle.label | sourceStr | +| UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | semmle.label | sink | +subpaths +#select +| UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | UncontrolledAllocationSizeBad.go:11:12:11:16 | selection of URL | UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | This memory allocation depends on a $@. | UncontrolledAllocationSizeBad.go:11:12:11:16 | selection of URL | user-provided value | diff --git a/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSize.qlref b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSize.qlref new file mode 100644 index 0000000..d40f385 --- /dev/null +++ b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSize.qlref @@ -0,0 +1 @@ +UncontrolledAllocationSize/UncontrolledAllocationSize.ql diff --git a/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSizeBad.go b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSizeBad.go new file mode 100644 index 0000000..e3fcc4e --- /dev/null +++ b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSizeBad.go @@ -0,0 +1,26 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" +) + +func OutOfMemoryBad(w http.ResponseWriter, r *http.Request) { + source := r.URL.Query() + // Get user-controlled input + sourceStr := source.Get("size") + sink, err := strconv.Atoi(sourceStr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + // BAD: Uncontrolled allocation size from user input + result := make([]string, sink) + for i := 0; i < sink; i++ { + result[i] = fmt.Sprintf("Item %d", i+1) + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} diff --git a/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSizeGood.go b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSizeGood.go new file mode 100644 index 0000000..2adfd2c --- /dev/null +++ b/languages/go/custom/test/UncontrolledAllocationSize/UncontrolledAllocationSizeGood.go @@ -0,0 +1,31 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" +) + +func OutOfMemoryGood(w http.ResponseWriter, r *http.Request) { + source := r.URL.Query() + MaxValue := 100 + // Get user-controlled input + sourceStr := source.Get("size") + sink, err := strconv.Atoi(sourceStr) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + // GOOD: Validate the size before allocation + if sink < 0 || sink > MaxValue { + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + result := make([]string, sink) + for i := 0; i < sink; i++ { + result[i] = fmt.Sprintf("Item %d", i+1) + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} diff --git a/languages/go/custom/test/UncontrolledAllocationSize/go.mod b/languages/go/custom/test/UncontrolledAllocationSize/go.mod new file mode 100644 index 0000000..45de446 --- /dev/null +++ b/languages/go/custom/test/UncontrolledAllocationSize/go.mod @@ -0,0 +1,3 @@ +module test + +go 1.21