taint

package module
v0.0.0-...-3011e23 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 26, 2025 License: MPL-2.0 Imports: 6 Imported by: 0

README

taint

Implements static taint analysis for Go programs.

Taint analysis is a technique for identifying the flow of sensitive data through a program. It can be used to identify potential security vulnerabilities, such as SQL injection or cross-site scripting (XSS) attacks, by understanding how this data is used and transformed as it flows through the code.

A "source" is a point in the program where sensitive data originates, typically from user input, such as data entered into a form on a web page, or data loaded from an external source. A "sink" is a point in the program where sensitive data is used or transmitted to exploit the program.

Example

This code generates a function call graph rooted at a program's main function and then runs taint analysis on it. If the program uses database/sql, the taint analysis will determine if the program is vulnerable to SQL injection such that any of the given sources reach the given sinks.

cg, _ := callgraph.New(mainFn, buildSSA.SrcFuncs...)

sources := taint.NewSources(
        "*net/http.Request",
        "google.golang.org/protobuf/proto.Message", // gRPC request types
)

sinks := taint.NewSinks(
	"(*database/sql.DB).Query",
	"(*database/sql.DB).QueryContext",
	"(*database/sql.DB).QueryRow",
	"(*database/sql.DB).QueryRowContext",
	"(*database/sql.Tx).Query",
	"(*database/sql.Tx).QueryContext",
	"(*database/sql.Tx).QueryRow",
	"(*database/sql.Tx).QueryRowContext",
)

results, _ := taint.Check(cg, sources, sinks)

for _, result := range results {
	// We found a query edge that is tainted by user input, is it
	// doing this safely? We expect this to be safely done by
	// providing a prepared statement as a constant in the query
	// (first argument after context).
	queryEdge := result.Path[len(result.Path)-1]

	// Get the query arguments, skipping the first element, pointer to the DB.
	queryArgs := queryEdge.Site.Common().Args[1:]

	// Skip the context argument, if using a *Context query variant.
	if strings.HasSuffix(queryEdge.Site.Value().Call.Value.String(), "Context") {
		queryArgs = queryArgs[1:]
	}

	// Get the query function parameter.
	query := queryArgs[0]

	// Ensure it is a constant (prepared statement), otherwise report
	// potential SQL injection.
	if _, isConst := query.(*ssa.Const); !isConst {
		pass.Reportf(result.SinkValue.Pos(), "potential sql injection")
	}
}
taint

The taint CLI is an interactive tool to find potential security vulnerabilities. It can be used to find potential SQL injections, log injections, and cross-site scripting (XSS) vulnerabilities, among other types of vulnerabilities.

$ go install github.com/picatz/taint/cmd/taint@latest

demo

Interactive Commands

The taint tool provides several interactive commands for exploring callgraphs and analyzing code:

load <target> [pattern] [--full] - Load a Go program or package for analysis

Targets can be local directories or GitHub URLs. When providing a GitHub URL you may optionally append a subdirectory or specific file path to restrict loading. This helps when large repositories contain many commands / main packages and you only want to look at a single entry point.

  • load ./myproject loads the local module using pattern ./... (recursive) by default.
  • load https://github.com/user/repo clones (shallow) and loads only the root package (.) by default, not the whole repo.
  • load https://github.com/user/repo/cmd/tool clones and loads ./cmd/tool (pattern defaults to . under that path).

callpath <function> - Find call paths to functions with flexible matching strategies:

> callpath fmt.Printf                    # Exact match
> callpath fuzzy:Printf                  # Substring/fuzzy matching  
> callpath glob:fmt.*                    # Shell-style glob patterns
> callpath regex:.*\.(Exec|Query)$       # Regular expressions

Example output:

> load xss
✓ loaded 1 packages, creating 1 SSA packages
✓ created multi-root callgraph with 3 potential roots

> callpath fuzzy:Check
✓ found 1 path(s) using fuzzy matching for: fuzzy:Check
1: n0:github.com/picatz/taint/xss.run → n8:github.com/picatz/taint.Check

> callpath fuzzy:run  
✓ found 2 path(s) using fuzzy matching for: fuzzy:run
1: n0:github.com/picatz/taint/xss.run (root node)
2: n0:github.com/picatz/taint/xss.run → n9:github.com/picatz/taint.WalkSSA → n10:github.com/picatz/taint/xss.run$1

check <source> <sink> - Perform taint analysis between source and sink functions

> load ./cmd/taint/example
✓ loaded 1 packages, creating 1 SSA packages
  building SSA package 0: main
✓ found main function, using as callgraph root
✓ loaded 1 packages
> cg
n0:github.com/picatz/taint/cmd/taint/example.main
   → n4:database/sql.Open
   → n5:net/http.NewServeMux
   → n6:(*net/http.ServeMux).HandleFunc
   → n8:net/http.ListenAndServe

n1:github.com/picatz/taint/cmd/taint/example.handle
   → n3:(*database/sql.DB).Query

n2:github.com/picatz/taint/cmd/taint/example.business
   → n1:github.com/picatz/taint/cmd/taint/example.handle

n3:(*database/sql.DB).Query

n4:database/sql.Open

n5:net/http.NewServeMux

n6:(*net/http.ServeMux).HandleFunc
   → n7:github.com/picatz/taint/cmd/taint/example.main$1

n7:github.com/picatz/taint/cmd/taint/example.main$1
   → n9:(*net/url.URL).Query
   → n2:github.com/picatz/taint/cmd/taint/example.business

n8:net/http.ListenAndServe

n9:(*net/url.URL).Query

> check *net/http.Request (*database/sql.DB).Query
n0:github.com/picatz/taint/cmd/taint/example.main → n6:(*net/http.ServeMux).HandleFunc → n7:github.com/picatz/taint/cmd/taint/example.main$1 → n2:github.com/picatz/taint/cmd/taint/example.business → n1:github.com/picatz/taint/cmd/taint/example.handle → n3:(*database/sql.DB).Query
> check (*net/url.URL).Query (*database/sql.DB).Query
n0:github.com/picatz/taint/cmd/taint/example.main → n6:(*net/http.ServeMux).HandleFunc → n7:github.com/picatz/taint/cmd/taint/example.main$1 → n2:github.com/picatz/taint/cmd/taint/example.business → n1:github.com/picatz/taint/cmd/taint/example.handle → n3:(*database/sql.DB).Query

Other commands: cg (show callgraph), nodes (list nodes), pkgs (list packages), root (show root), clear, exit

sqli

The sqli analyzer finds potential SQL injections.

Supported SQL packages include:

  • the standard library database/sql package
  • github.com/jinzhu/gorm (GORM v1)
  • gorm.io/gorm (GORM v2)
  • github.com/jmoiron/sqlx
  • github.com/go-gorm/gorm (GORM v2 alt)
  • xorm.io/xorm and github.com/go-xorm/xorm
  • github.com/go-pg/pg
  • github.com/rqlite/gorqlite
  • github.com/raindog308/gorqlite
  • github.com/Masterminds/squirrel and variants
  • database drivers like github.com/mattn/go-sqlite3
$ go install github.com/picatz/taint/cmd/sqli@latest
$ cd sql/injection/testdata/src/example
$ cat main.go
package main

import (
        "database/sql"
        "net/http"
)

func business(db *sql.DB, q string) {
        db.Query(q) // potential sql injection
}

func run() {
        db, _ := sql.Open("sqlite3", ":memory:")

        mux := http.NewServeMux()

        mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                business(db, r.URL.Query().Get("sql-query"))
        })

        http.ListenAndServe(":8080", mux)
}

func main() {
        run()
}
$ sqli main.go
./sql/injection/testdata/src/example/main.go:9:10: potential sql injection
logi

The logi analyzer finds potential log injections. It understands common logging packages, including log, log/slog, github.com/golang/glog, github.com/hashicorp/go-hclog, github.com/sirupsen/logrus, and go.uber.org/zap.

$ go install github.com/picatz/taint/cmd/logi@latest
$ cd log/injection/testdata/src/a
$ cat main.go
package main

import (
        "log"
        "net/http"
)

func main() {
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                log.Println(r.URL.Query().Get("input"))
        })

        http.ListenAndServe(":8080", nil)
}
$ logi main.go
./log/injection/testdata/src/a/main.go:10:14: potential log injection
xss

The xss analyzer finds potential cross-site scripting (XSS) vulnerabilities.

$ go install github.com/picatz/taint/cmd/xss@latest
$ cd xss/testdata/src/a
$ cat main.go
package main

import (
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(r.URL.Query().Get("input"))) // want "potential XSS"
	})

	http.ListenAndServe(":8080", nil)
}
$ xss main.go
./xss/testdata/src/a/main.go:9:10: potential XSS

Documentation

Overview

Package taint enables "taint checking", a static analysis technique for identifying attacker-controlled "sources" used in dangerous contexts "sinks".

A classic example of this is identifying SQL injections, where user controlled inputs, typically from an HTTP request, finds their way into a SQL query without using a prepared statement.

Index

Constants

This section is empty.

Variables

View Source
var ErrStopWalk = fmt.Errorf("taint: stop walk")

Functions

func WalkSSA

func WalkSSA(v ssa.Value, visit func(v ssa.Value) error) error

WalkSSA walks the SSA IR recursively with a visitor function that can be used to inspect each node in the graph. The visitor function should return an error if it wants to stop the walk.

Types

type Result

type Result struct {
	// Path is the specific path within a callgraph
	// where the source founds its way into a sink.
	Path callgraphutil.Path

	// Source type information.
	SourceType string
	// Source SSA value.
	SourceValue ssa.Value

	// Sink information.
	SinkType string
	// Sink SSA value.
	SinkValue ssa.Value
}

It contains the path within the callgraph where the source found its way into the sink, along with the source and sink type information and SSA values.

type Results

type Results []Result

Results is a collection of unique findings from a taint check.

func Check

func Check(cg *callgraph.Graph, sources Sources, sinks Sinks) Results

Check is the primary function users of this package will use.

It returns a list of results from the callgraph, where any of the given sources found their way into any of the given sinks.

Sources is a list of functions that return user-controlled values, such as HTTP request parameters. Sinks is a list of potentially dangerous functions that should not be called with user-controlled values.

Diagram
             ╭───────────────────────────────────────────────────────────────╮
             │                          ╭────────┬──────────────╮            │
             │                          ▼        │              │            │
╭───────╮    │ ╭───────────╮    ╭───────────────╮│   ╭──────────┴──────────╮ │
│ Check ├──▶ │ │ checkPath ├──▶ │ checkSSAValue ├┴─▶ │ checkSSAInstruction │ │
╰───────╯    │ ╰───────────╯    ╰───────────────╯    ╰─────────────────────╯ │
             ╰──────────────────────────────┬────────────────────────────────╯
                                            │
                                            ▼
                                       ╭─────────╮
                                       │ Results │
                                       ╰─────────╯

This is a recursive algorithm that will traverse the callgraph to identify if any of the given sources were used to obtain the initial SSA value (v). We handle this value, depending on its type, where we "peel back" its references and relevant SSA instructions to determine if any of the given sinks were involved in the creation of the initial value.

type Sinks

type Sinks = stringSet

Sinks are the types that are considered "sinks" that tainted data in the program may flow into.

func NewSinks

func NewSinks(sinkTypes ...string) Sinks

NewSinks returns a new Sinks set with the given sink types.

type Sources

type Sources = stringSet

Sources are the types that are considered "sources" of tainted data in the program.

func NewSources

func NewSources(sourceTypes ...string) Sources

NewSources returns a new Sources set with the given source types.

Directories

Path Synopsis
Package callgraphutil implements utilities for golang.org/x/tools/go/callgraph including path searching, graph construction, printing, and more.
Package callgraphutil implements utilities for golang.org/x/tools/go/callgraph including path searching, graph construction, printing, and more.
cmd
logi command
sqli command
ssadump command
taint command
taint/example command
xss command
log
sql

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL