Skip to content

Commit

Permalink
Merge pull request #17335 from egregius313/egregius313/go/dataflow/mo…
Browse files Browse the repository at this point in the history
…dels/stdin

Go: Implement `stdin` models
  • Loading branch information
egregius313 authored Oct 14, 2024
2 parents 96ea950 + 0abc0d1 commit ade5686
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 0 deletions.
4 changes: 4 additions & 0 deletions go/ql/lib/change-notes/2024-08-29-add-stdin-threat-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Local source models with the `stdin` source kind have been added for the variable `os.Stdin` and the functions `fmt.Scan`, `fmt.Scanf` and `fmt.Scanln`. You can optionally include threat models as appropriate when using the CodeQL CLI and in GitHub code scanning. For more information, see [Analyzing your code with CodeQL queries](https://docs.github.com/code-security/codeql-cli/getting-started-with-the-codeql-cli/analyzing-your-code-with-codeql-queries#including-model-packs-to-add-potential-sources-of-tainted-data>) and [Customizing your advanced setup for code scanning](https://docs.github.com/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#extending-codeql-coverage-with-threat-models).
9 changes: 9 additions & 0 deletions go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ module Fmt {
Scanner() { this.hasQualifiedName("fmt", ["Scan", "Scanf", "Scanln"]) }
}

private class ScannerSource extends SourceNode {
ScannerSource() {
// All of the arguments which are sources are varargs.
this.asExpr() = any(Scanner s).getACall().getAnImplicitVarargsArgument().asExpr()
}

override string getThreatModel() { result = "stdin" }
}

/**
* The `Fscan` function or one of its variants,
* all of which read from a specified `io.Reader`.
Expand Down
8 changes: 8 additions & 0 deletions go/ql/lib/semmle/go/frameworks/stdlib/Os.qll
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,12 @@ module Os {
input = inp and output = outp
}
}

private class Stdin extends SourceNode {
Stdin() {
exists(Variable osStdin | osStdin.hasQualifiedName("os", "Stdin") | this = osStdin.getARead())
}

override string getThreatModel() { result = "stdin" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module test

go 1.22.6
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
testFailures
invalidModelRow
failures
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extensions:

- addsTo:
pack: codeql/threat-models
extensible: threatModelConfiguration
data:
- ["stdin", true, 0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import go
import ModelValidation
import TestUtilities.InlineExpectationsTest

module SourceTest implements TestSig {
string getARelevantTag() { result = "source" }

predicate hasActualResult(Location location, string element, string tag, string value) {
exists(ActiveThreatModelSource s |
s.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
element = s.toString() and
value = "" and
tag = "source"
)
}
}

import MakeTest<SourceTest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
testFailures
invalidModelRow
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extensions:

- addsTo:
pack: codeql/threat-models
extensible: threatModelConfiguration
data:
- ["stdin", true, 0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package test

import (
"bufio"
"fmt"
"os"
)

func sink(string) {

}

func readStdinBuffer() {
buf := make([]byte, 1024)
n, err := os.Stdin.Read(buf) // $source
if err != nil {
return
}
sink(string(buf[:n])) // $hasTaintFlow="type conversion"
}

func readStdinBuffReader() {
buf := make([]byte, 1024)
r := bufio.NewReader(os.Stdin) // $source
n, err := r.Read(buf)
if err != nil {
return
}
sink(string(buf[:n])) // $hasTaintFlow="type conversion"
}

func scan() {
var username, email string
fmt.Scan(&username, &email) // $source
sink(username) // $hasTaintFlow="username"
}

func scanf() {
var s string
fmt.Scanf("%s", &s) // $source
sink(s) // $hasTaintFlow="s"
}

func scanl() {
var s string
fmt.Scanln(&s) // $source
sink(s) // $hasTaintFlow="s"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import go
import semmle.go.dataflow.ExternalFlow
import ModelValidation
import experimental.frameworks.CleverGo
import TestUtilities.InlineFlowTest

module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource }

predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(CallExpr c | c.getTarget().getName() = "sink").getArgument(0)
}
}

import TaintFlowTest<Config>

0 comments on commit ade5686

Please sign in to comment.