chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// flowDaemonTool is the daemon-tool relay seam. It is indirected through a
|
||||
// package var so tests can stub the daemon call (asserting the lowered
|
||||
// tool + args) without a running daemon. Shared by `flow` and `taint`.
|
||||
var flowDaemonTool = requireDaemonTool
|
||||
|
||||
// --- flow (flow_between) ----------------------------------------------------
|
||||
|
||||
var (
|
||||
flowIndex string
|
||||
flowFrom string
|
||||
flowTo string
|
||||
flowMaxDepth int
|
||||
flowMaxPaths int
|
||||
flowMinTier string
|
||||
flowFormat string
|
||||
)
|
||||
|
||||
var flowCmd = &cobra.Command{
|
||||
Use: "flow",
|
||||
Short: "Trace ranked dataflow paths between two symbols (flow_between)",
|
||||
Long: `Walks the CPG-lite dataflow graph (value-flow / arg-of / returns-to edges)
|
||||
forward from --from to --to and returns the ranked paths — the primitive that
|
||||
answers "where does this value flow?". Pair with 'gortex taint' for the
|
||||
pattern-driven sweep.
|
||||
|
||||
gortex flow --from pkg/a.go::Input --to pkg/b.go::Sink --max-depth 6
|
||||
|
||||
Requires a running daemon that tracks the repo.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if flowFrom == "" || flowTo == "" {
|
||||
return fmt.Errorf("--from and --to are required (source and sink symbol IDs)")
|
||||
}
|
||||
toolArgs := map[string]any{
|
||||
"source_id": flowFrom,
|
||||
"sink_id": flowTo,
|
||||
}
|
||||
if cmd.Flags().Changed("max-depth") {
|
||||
toolArgs["max_depth"] = flowMaxDepth
|
||||
}
|
||||
if cmd.Flags().Changed("max-paths") {
|
||||
toolArgs["max_paths"] = flowMaxPaths
|
||||
}
|
||||
if cmd.Flags().Changed("min-tier") {
|
||||
toolArgs["min_tier"] = flowMinTier
|
||||
}
|
||||
return runFlowTool(cmd, "flow_between", toolArgs, flowFormat)
|
||||
},
|
||||
}
|
||||
|
||||
// --- taint (taint_paths) ----------------------------------------------------
|
||||
|
||||
var (
|
||||
taintIndex string
|
||||
taintSource string
|
||||
taintSink string
|
||||
taintMaxDepth int
|
||||
taintLimit int
|
||||
taintMinTier string
|
||||
taintFormat string
|
||||
)
|
||||
|
||||
var taintCmd = &cobra.Command{
|
||||
Use: "taint",
|
||||
Short: "Pattern-driven dataflow sweep from matching sources to sinks (taint_paths)",
|
||||
Long: `Resolves every symbol matching --source and --sink, then walks the dataflow
|
||||
graph to find paths between each pair — the security-audit primitive ("every
|
||||
flow from os.Getenv to db.Query"). Pattern syntax: a bare token is a
|
||||
case-insensitive substring on the symbol name; exact:Foo matches exactly;
|
||||
path:dir/ filters by file-path prefix; kind:method restricts the node kind.
|
||||
Combine clauses with spaces.
|
||||
|
||||
gortex taint --source 'path:handlers/' --sink 'exact:Exec' --limit 30
|
||||
|
||||
Requires a running daemon that tracks the repo.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if taintSource == "" || taintSink == "" {
|
||||
return fmt.Errorf("--source and --sink are required (source and sink patterns)")
|
||||
}
|
||||
toolArgs := map[string]any{
|
||||
"source_pattern": taintSource,
|
||||
"sink_pattern": taintSink,
|
||||
}
|
||||
if cmd.Flags().Changed("max-depth") {
|
||||
toolArgs["max_depth"] = taintMaxDepth
|
||||
}
|
||||
if cmd.Flags().Changed("limit") {
|
||||
toolArgs["limit"] = taintLimit
|
||||
}
|
||||
if cmd.Flags().Changed("min-tier") {
|
||||
toolArgs["min_tier"] = taintMinTier
|
||||
}
|
||||
return runFlowTool(cmd, "taint_paths", toolArgs, taintFormat)
|
||||
},
|
||||
}
|
||||
|
||||
// runFlowTool calls a dataflow tool and renders the result. gcx/toon are printed
|
||||
// verbatim (re-indenting would corrupt them); every other format pretty-prints
|
||||
// the JSON via emitDaemonJSON. The repo path comes from the per-command --index.
|
||||
func runFlowTool(cmd *cobra.Command, tool string, args map[string]any, format string) error {
|
||||
repo := flowIndex
|
||||
if tool == "taint_paths" {
|
||||
repo = taintIndex
|
||||
}
|
||||
if format != "" {
|
||||
args["format"] = format
|
||||
}
|
||||
raw, err := flowDaemonTool(repo, tool, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch format {
|
||||
case "gcx", "toon":
|
||||
fmt.Fprintln(cmd.OutOrStdout(), strings.TrimRight(string(raw), "\n"))
|
||||
return nil
|
||||
default:
|
||||
return emitDaemonJSON(cmd, raw)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
flowCmd.Flags().StringVar(&flowIndex, "index", ".", "repository path the daemon must track")
|
||||
flowCmd.Flags().StringVar(&flowIndex, "repo", ".", "alias for --index")
|
||||
flowCmd.Flags().StringVar(&flowFrom, "from", "", "source symbol node ID (source_id)")
|
||||
flowCmd.Flags().StringVar(&flowTo, "to", "", "sink symbol node ID (sink_id)")
|
||||
flowCmd.Flags().IntVar(&flowMaxDepth, "max-depth", 0, "maximum BFS hops (max_depth; default 8)")
|
||||
flowCmd.Flags().IntVar(&flowMaxPaths, "max-paths", 0, "maximum paths to return (max_paths; default 10)")
|
||||
flowCmd.Flags().StringVar(&flowMinTier, "min-tier", "", "minimum per-edge Origin tier to traverse (min_tier)")
|
||||
flowCmd.Flags().StringVar(&flowFormat, "format", "json", "output / wire format: json|gcx|toon|text")
|
||||
rootCmd.AddCommand(flowCmd)
|
||||
|
||||
taintCmd.Flags().StringVar(&taintIndex, "index", ".", "repository path the daemon must track")
|
||||
taintCmd.Flags().StringVar(&taintIndex, "repo", ".", "alias for --index")
|
||||
taintCmd.Flags().StringVar(&taintSource, "source", "", "source pattern (source_pattern)")
|
||||
taintCmd.Flags().StringVar(&taintSink, "sink", "", "sink pattern (sink_pattern)")
|
||||
taintCmd.Flags().IntVar(&taintMaxDepth, "max-depth", 0, "maximum BFS hops per pair (max_depth; default 8)")
|
||||
taintCmd.Flags().IntVar(&taintLimit, "limit", 0, "maximum findings to return (default 20)")
|
||||
taintCmd.Flags().StringVar(&taintMinTier, "min-tier", "", "minimum per-edge Origin tier to traverse (min_tier)")
|
||||
taintCmd.Flags().StringVar(&taintFormat, "format", "json", "output / wire format: json|gcx|toon|text")
|
||||
rootCmd.AddCommand(taintCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user