Files
wehub-resource-sync a06f331eb8
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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

152 lines
5.4 KiB
Go

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)
}