cli

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Dec 11, 2025 License: Apache-2.0 Imports: 43 Imported by: 0

Documentation

Overview

Package cli implements the interactive argot CLI.

Package cli implements the the Argot interactive CLI: a terminal application that lets you run most of the analyses that are available in argot, with additional functionality to print verbose output and with commands to help you understand, and possibly debug your analyses.

Usage:

argot cli [flags] -config config-file.yaml [program files]

The flags are:

-verbose=false
	verbose mode, overrides any verbose option specified in the config file
-config config-file.yaml
	a configuration file for the analyses. The configuration file can be empty, but should always be specified.
	If [program files] is only one main.go file, the program will look for a file config.yaml in the same folder
	as the config file.

There are 29 commands you can use once you have started the argot-cli and your program has been loaded and some information has been collected.

Basic Commands

There are a few basic commands:

help             print a list of the commands, with short help messages for each

cd,ls            cd and ls allow you to change the working directory of the terminal and list its contents

exit             exit exits the program gracefully

state?           show a summary of the state, including path to config file and program

stats            show some statistics about the current program

Commands that let you manipulate the state, reloading programs and configuration files:

load  path       load a program, the path is the same type of argument you would pass to the argot-cli initially

rebuild          reload the current program and rebuild it

reconfig [path]  reload the config file, or load the config file at path if specified

Inspecting Functions

Commands that let you inspect the functions in the program:

list [name]        list all the functions matching name, or all loaded functions if no name is specified
.                  Flag -r shows only reachable functions, and -s only summarized functions.

where "name"	   show the locations of all the function matching name

callees "name"     print the list of callees the functions matching name

callers "name"     print the list of callers of the functions matching name

showssa "name"     print the SSA form of the functions matching name

Running Analyses

Commands to run analyses and inspect resulting information:

summarize [name]    summarize all functions matching name, or every reachable function if no name is supplied

summary "name"      print the summary of all functions matching name, if any

buildgraph          builds the cross-cross function graph. You must first use `summarize` to build summaries

taint               run the taint analysis

trace               run a dataflow graph exploration (taint analysis from any node given its id)

showdataflow        build and print the dataflow graph of a program

showescape "name"   print the escape graph of all functions matching name

Focused Mode

Commands to use in "focused" mode, which lets you focus on a particular function and obtained detailed information about that function:

focus "name"    	focus name focuses on a specific function and enters "focus" mode

unfocus           	exit "focus" mode

intra             	run the intra-procedural dataflow analysis and show its result.
.                 	The -v flag shows intermediate results

mayalias "value" 	show all the aliases of the values matching value

ssaval "value"	 	show information about all the values matching value in the function

ssainstr "instr" 	show information about all the instructions matching instr in the function

pkg                 print the name of the focuse function's package

The commands showssa, summary and where can be used without an argument in focused mode, in which case the function defaults to the currently focused function.

Index

Constants

View Source
const (
	// CmdAstName is the name of the ast command
	CmdAstName = "ast"
	// CmdBacktraceName is the name of the backtrace command
	CmdBacktraceName = "backtrace"
	// CmdBuildGraphName is the name of the buildgraph command
	CmdBuildGraphName = "buildgraph"
	// CmdCalleesName is the name of the callees command
	CmdCalleesName = "callees"
	// CmdCallersName is the name of the callers command
	CmdCallersName = "callers"
	// CmdCdName is the name of the cd command
	CmdCdName = "cd"
	// CmdExitName is the name of the exit command
	CmdExitName = "exit"
	// CmdFocusName is the name of the focus command
	CmdFocusName = "focus"
	// CmdHelpName is the name of the help command
	CmdHelpName = "help"
	// CmdIntraName is the name of the intra command
	CmdIntraName = "intra"
	// CmdLoadName is the name of the load command
	CmdLoadName = "load"
	// CmdLoadPackagesName is the name of the load-pkg command
	CmdLoadPackagesName = "load-pkg"
	// CmdLoadWholeProgramName is the name of the load-program command
	CmdLoadWholeProgramName = "load-program"
	// CmdListName is the name of the list command
	CmdListName = "list"
	// CmdLsName is the name of the ls command
	CmdLsName = "ls"
	// CmdMarkName is the name of the mark command
	CmdMarkName = "mark"
	// CmdMayAliasName is the name of the mayalias command
	CmdMayAliasName = "mayalias"
	// CmdPackageName is the name of the function-pkg command
	CmdPackageName = "function-pkg"
	// CmdRebuildName is the name of the rebuild command
	CmdRebuildName = "rebuild"
	// CmdReconfigName is the name of the reconfig command
	CmdReconfigName = "reconfig"
	// CmdScanName is the name of the scan command
	CmdScanName = "scan"
	// CmdShowPackageName is the name of the showpkg command
	CmdShowPackageName = "showpkg"
	// CmdShowDataflowName is the name of the showdataflow command
	CmdShowDataflowName = "showdataflow"
	// CmdShowEscapeName is the name of the showescape command
	CmdShowEscapeName = "showescape"
	// CmdMembersName is the name of the members command
	CmdMembersName = "members"
	// CmdRunPointerName is the name of the run-pointer command
	CmdRunPointerName = "run-pointer"
	// CmdRunDataflowName is the name of the run-dataflow command
	CmdRunDataflowName = "run-dataflow"
	// CmdShowSsaName is the name of the showssa command
	CmdShowSsaName = "showssa"
	// CmdSrcName is the name of the src command
	CmdSrcName = "src"
	// CmdSsaInstrName is the name of the ssainstr command
	CmdSsaInstrName = "ssainstr"
	// CmdSsaValueName is the name of the ssaval command
	CmdSsaValueName = "ssaval"
	// CmdStateName is the name of the state? command
	CmdStateName = "state?"
	// CmdStatsName is the name of the stats command
	CmdStatsName = "stats"
	// CmdSummarizeName is the name of the summarize command
	CmdSummarizeName = "summarize"
	// CmdSummaryName is the name of the summary command
	CmdSummaryName = "summary"
	// CmdTaintName is the name of the taint command
	CmdTaintName = "taint"
	// CmdTraceName is the name of the trace command
	CmdTraceName = "trace"
	// CmdUnfocusName is the name of the unfocus command
	CmdUnfocusName = "unfocus"
	// CmdWhereName is the name of the where command
	CmdWhereName = "where"
)
View Source
const Usage = `Interactive CLI for exploring the program and running various analyses.
Usage:
  argot cli [options] <package path(s)>`

Usage for CLI

Variables

View Source
var Commands = map[string]CommandDefinition{
	CmdAstName: {
		Name:        toolAstName,
		Description: "Print the AST of a function or file (shows detailed AST)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match function or file names",
				},
				"file": map[string]interface{}{
					"type":        "boolean",
					"description": "Print AST of file instead of function",
				},
			},
			Required: []string{"regex"},
		},

		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			fileFlag := false
			if file, ok := props["file"]; ok {
				fileFlag, ok = file.(bool)
				if !ok {
					return Command{}, fmt.Errorf("file must be a boolean")
				}
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{"file": fileFlag},
			}, nil
		},
		Function: cmdAst,
	},
	CmdBacktraceName: {
		Name:        toolBacktraceName,
		Description: "Run the backtrace analysis with parameters in config",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdBacktrace,
	},
	CmdBuildGraphName: {
		Name:        toolBuildGraphName,
		Description: "Build the inter-procedural dataflow graph",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdBuildGraph,
	},
	CmdCallersName: {
		Name:        toolCallersName,
		Description: "Show the callers of a function. Uses the best analysis loaded (e.g. pointer when loaded).",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match function names",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdCallers,
	},
	CmdCalleesName: {
		Name:        toolCalleesName,
		Description: "Show callees of a function. Uses the best analysis loaded (e.g. pointer when loaded).",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match function names",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdCallees,
	},
	CmdCdName: {
		Name:        toolCdName,
		Description: "Move to relative directory (system command)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"directory": map[string]interface{}{
					"type":        "string",
					"description": "Directory path to change to",
				},
			},
			Required: []string{"directory"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			directory, ok := props["directory"].(string)
			if !ok {
				return Command{}, fmt.Errorf("directory must be provided")
			}
			return Command{
				Args:  []string{directory},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdCd,
	},
	CmdExitName: {
		Name:        toolExitName,
		Description: "Exit the program (system command, terminates the server).",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdExit,
	},
	CmdFocusName: {
		Name:        toolFocusName,
		Description: "Focus on a specific function, so you can inspect ssa values, instructions, aliases...",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match function names",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdFocus,
	},
	CmdIntraName: {
		Name:        toolIntraName,
		Description: "Run intra-procedural dataflow analysis (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdIntra,
	},
	CmdListName: {
		Name: toolListName,
		Description: "List functions matching a regex (golang), " +
			"with options on listing reachable and summarized functions." +
			"Matches against the full function name (package name + opt. receiver + function name)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match full function names",
				},
				"reachable_only": map[string]interface{}{
					"type":        "boolean",
					"description": "List only reachable functions",
				},
				"summarized_only": map[string]interface{}{
					"type":        "boolean",
					"description": "List only summarized functions",
				},
			},
			Required: []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex := ""
			if regexObj, ok := props["regex"]; ok {
				if regexStr, ok := regexObj.(string); ok {
					regex = regexStr
				} else {
					return Command{}, fmt.Errorf("regex must be a string")
				}
			}
			flags := map[string]bool{}
			if reachableOnly, ok := props["reachable_only"]; ok {
				if reachableBool, ok := reachableOnly.(bool); ok {
					flags["r"] = reachableBool
				} else {
					return Command{}, fmt.Errorf("reachable_only must be a boolean")
				}
			}
			if summarizedOnly, ok := props["summarized_only"]; ok {
				if summarizedBool, ok := summarizedOnly.(bool); ok {
					flags["s"] = summarizedBool
				} else {
					return Command{}, fmt.Errorf("summarized_only must be a boolean")
				}
			}
			args := []string{}
			if regex != "" {
				args = []string{regex}
			}
			return Command{
				Args:  args,
				Flags: flags,
			}, nil
		},
		Function: cmdList,
	},
	CmdLoadName: {
		Name:        toolLoadName,
		Description: "Load new program from package paths. This resets the analysis state (pointer, dataflow, ..)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"paths": map[string]interface{}{
					"type":        "array",
					"items":       map[string]interface{}{"type": "string"},
					"description": "Paths to load (files or directory)",
				},
				"target": map[string]interface{}{
					"type":        "string",
					"description": "Target to load (state? shows available targets from config, required loaded config)",
				},
			},
			Required: []string{"paths"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			packagesObj, ok := props["paths"]
			if !ok {
				return Command{}, fmt.Errorf("paths must be provided")
			}
			packages, ok := packagesObj.([]interface{})
			if !ok {
				return Command{}, fmt.Errorf("paths must be an array")
			}
			args := make([]string, len(packages))
			for i, pkg := range packages {
				args[i], ok = pkg.(string)
				if !ok {
					return Command{}, fmt.Errorf("path must be a string")
				}
			}
			namedArgs := map[string]string{}
			if target, ok := props["target"]; ok {
				if targetStr, ok := target.(string); ok {
					namedArgs["target"] = targetStr
				} else {
					return Command{}, fmt.Errorf("target must be a string")
				}
			}
			return Command{
				Args:      args,
				Flags:     map[string]bool{},
				NamedArgs: namedArgs,
			}, nil
		},
		Function: cmdLoad,
	},
	CmdLoadPackagesName: {
		Name: toolLoadPackagesName,
		Description: "Load Go packages with optional type information for inspection." +
			" This is faster than loading a whole program, and doesn't require loading the main entry point." +
			"However, dataflow and pointer analyses will not be available.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"packages": map[string]interface{}{
					"type":        "array",
					"items":       map[string]interface{}{"type": "string"},
					"description": "Package paths to load",
				},
				"with_types": map[string]interface{}{
					"type":        "boolean",
					"description": "Load packages with types",
				},
			},
			Required: []string{"packages"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			packagesObj, ok := props["packages"]
			if !ok {
				return Command{}, fmt.Errorf("packages must be provided")
			}
			packages, ok := packagesObj.([]interface{})
			if !ok {
				return Command{}, fmt.Errorf("packages must be an array")
			}
			args := make([]string, len(packages))
			for i, pkg := range packages {
				args[i], ok = pkg.(string)
				if !ok {
					return Command{}, fmt.Errorf("package must be a string")
				}
			}
			flags := map[string]bool{}
			if withTypes, ok := props["with_types"]; ok {
				if withTypesBool, ok := withTypes.(bool); ok {
					flags["t"] = withTypesBool
				} else {
					return Command{}, fmt.Errorf("with_types must be a boolean")
				}
			}
			return Command{
				Args:  args,
				Flags: flags,
			}, nil
		},
		Function: cmdLoadPackages,
	},
	CmdLsName: {
		Name:        toolLsName,
		Description: "List files in directory (system command)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"path": map[string]interface{}{
					"type":        "string",
					"description": "Path to list (defaults to current directory)",
				},
			},
			Required: []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			path := "."
			if pathObj, ok := props["path"]; ok {
				if pathStr, ok := pathObj.(string); ok {
					path = pathStr
				} else {
					return Command{}, fmt.Errorf("path must be a string")
				}
			}
			return Command{
				Args:  []string{path},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdLs,
	},
	CmdMarkName: {
		Name:        toolMarkName,
		Description: "Show dataflow marks and associated instructions (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match names",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdMark,
	},
	CmdMayAliasName: {
		Name:        toolMayAliasName,
		Description: "Check if values may alias (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"values": map[string]interface{}{
					"type":        "string",
					"description": "Regex matching values to check for aliasing.",
				},
			},
			Required: []string{"values"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			if values, ok := props["values"].(string); ok {
				return Command{
					Args:  []string{values},
					Flags: map[string]bool{},
				}, nil
			}
			return Command{}, fmt.Errorf("values must be a string")
		},
		Function: cmdMayAlias,
	},
	CmdPackageName: {
		Name:        toolPackageName,
		Description: "Show package information of focused function (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match package names",
				},
			},
			Required: []string{},
		},

		Function: cmdPackage,
	},
	CmdRebuildName: {
		Name:        toolRebuildName,
		Description: "Rebuild the program being analyzed. Re-initializes the complex analyses!",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		Function: cmdRebuild,
	},
	CmdReconfigName: {
		Name:        toolReconfigName,
		Description: "Reload current config or load new configuration file. Re-initializes all the analyses.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"config_file": map[string]interface{}{
					"type":        "string",
					"description": "Path to config file",
				},
			},
			Required: []string{"config_file"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			configFile, ok := props["config_file"].(string)
			if !ok || configFile == "" {
				return Command{}, fmt.Errorf("config_file must be a non-empty string")
			}
			return Command{
				Args:  []string{configFile},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdReconfig,
	},
	CmdScanName: {
		Name:        toolScanName,
		Description: "Scan AST for identifiers and types matching regex patterns.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"pattern": map[string]interface{}{
					"type":        "string",
					"description": "Pattern to scan for",
				},
			},
			Required: []string{"pattern"},
		},
		Function: cmdScan,
	},
	CmdShowPackageName: {
		Name:        toolShowPackageName,
		Description: "Show detailed information about a loaded package including files and imports.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match package names",
				},
			},
			Required: []string{},
		},
		Function: cmdShowPackage,
	},
	CmdShowSsaName: {
		Name:        toolShowSsaName,
		Description: "Print the SSA representation of a function.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match full function names",
				},
			},
			Required: []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if ok && regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string if provided")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdShowSsa,
	},
	CmdShowEscapeName: {
		Name:        toolShowEscapeName,
		Description: "Print the escape graph of a function",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match full function names",
				},
			},
			Required: []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdShowEscape,
	},
	CmdMembersName: {
		Name:        toolMembersName,
		Description: "Print package members (functions, types, constants, globals) matching a regex",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match package names",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdMembers,
	},
	CmdRunDataflowName: {
		Name:        toolRunDataflowName,
		Description: "Run the dataflow analysis (advanced analysis)",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdRunDataflow,
	},
	CmdRunPointerName: {
		Name:        toolRunPointerName,
		Description: "Run the pointer analysis (advanced analysis, use to get callees)",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdRunPointer,
	},
	CmdShowDataflowName: {
		Name:        toolShowDataflowName,
		Description: "Build and print the inter-procedural dataflow graph (advanced analysis, run summarize first)",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdShowDataflow,
	},
	CmdSrcName: {
		Name:        toolSrcName,
		Description: "Print the source code of a function.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match full function names",
				},
			},
			Required: []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdSrc,
	},
	CmdSsaInstrName: {
		Name:        toolSsaInstrName,
		Description: "Show SSA instruction details (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"instruction": map[string]interface{}{
					"type":        "string",
					"description": "SSA instruction to examine",
				},
			},
			Required: []string{"instruction"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			instruction, ok := props["instruction"].(string)
			if !ok || instruction == "" {
				return Command{}, fmt.Errorf("instruction must be a non-empty string")
			}
			return Command{
				Args:  []string{instruction},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdSsaInstr,
	},
	CmdSsaValueName: {
		Name:        toolSsaValueName,
		Description: "Show SSA value details (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"value": map[string]interface{}{
					"type":        "string",
					"description": "SSA value to examine",
				},
			},
			Required: []string{"value"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			value, ok := props["value"].(string)
			if !ok || value == "" {
				return Command{}, fmt.Errorf("value must be a non-empty string")
			}
			return Command{
				Args:  []string{value},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdSsaValue,
	},
	CmdStateName: {
		Name:        toolStateName,
		Description: "Show current analysis state.",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdState,
	},
	CmdStatsName: {
		Name:        toolStatsName,
		Description: "Display comprehensive program statistics including SSA, defers, and closure usage.",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdStats,
	},
	CmdSummaryName: {
		Name: toolSummaryName,
		Description: "Print the internal dataflow summary of functions matching a regex. " +
			"You should have run summarize on that function first (or on all functions)." +
			"Running this may be very expensive!",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match full function names",
				},
				"filter": map[string]interface{}{
					"type":        "string",
					"description": "Filter for summary display",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			regex, ok := props["regex"].(string)
			if !ok || regex == "" {
				return Command{}, fmt.Errorf("regex must be a non-empty string")
			}
			namedArgs := map[string]string{}
			filter, ok := props["filter"].(string)
			if ok {
				namedArgs["f"] = filter
			}
			return Command{
				Args:      []string{regex, filter},
				Flags:     map[string]bool{},
				NamedArgs: namedArgs,
			}, nil
		},
		Function: cmdSummary,
	},
	CmdSummarizeName: {
		Name: toolSummarizeName,
		Description: "Build dataflow summaries for functions using intra-procedural analysis." +
			"Depending on internal parameters, running the tool without arguments may only instantiate " +
			"the summaries without building them.",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"regex": map[string]interface{}{
					"type":        "string",
					"description": "Regex to match full function names (optional)",
				},
				"force": map[string]interface{}{
					"type":        "boolean",
					"description": "Force summarization and bypass filters",
				},
			},
			Required: []string{"regex"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			forceFlag := false
			if force, ok := props["force"]; ok {
				if forceBool, ok := force.(bool); ok {
					forceFlag = forceBool
				} else {
					return Command{}, fmt.Errorf("force flag must be a boolean")
				}
			}
			regex := ""
			regexObj, ok := props["regex"]
			if !ok {

				return Command{}, fmt.Errorf("regex must be provided")
			}
			if ok {
				if regexStr, ok := regexObj.(string); ok {
					regex = regexStr
				} else {
					return Command{}, fmt.Errorf("regex must be a string")
				}
			}
			return Command{
				Args:  []string{regex},
				Flags: map[string]bool{"force": forceFlag},
			}, nil
		},
		Function: cmdSummarize,
	},
	CmdTaintName: {
		Name: toolTaintName,
		Description: "Run the taint analysis with parameters in config file. Does nothing if no config file has been" +
			"loaded, or the config file does not define any taint analysis problem.",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdTaint,
	},
	CmdTraceName: {
		Name:        toolTraceName,
		Description: "Trace dataflow paths for a specific SSA value through the program (advanced debugging analysis)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"target": map[string]interface{}{
					"type":        "string",
					"description": "Target to trace",
				},
			},
			Required: []string{"target"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			target, ok := props["target"].(string)
			if !ok || target == "" {
				return Command{}, fmt.Errorf("target must be a non-empty string")
			}
			return Command{
				Args:  []string{target},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdTrace,
	},
	CmdUnfocusName: {
		Name:        toolUnfocusName,
		Description: "Remove focus from current function (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type:       "object",
			Properties: map[string]interface{}{},
			Required:   []string{},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			return Command{
				Args:  []string{},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdUnfocus,
	},
	CmdWhereName: {
		Name:        toolWhereName,
		Description: "Show source file location of functions or current focused function (requires focused function)",
		InputSchema: tools.MCPInputSchema{
			Type: "object",
			Properties: map[string]interface{}{
				"target": map[string]interface{}{
					"type":        "string",
					"description": "Target to locate",
				},
			},
			Required: []string{"target"},
		},
		SchemaTranslation: func(props map[string]interface{}) (Command, error) {
			target, ok := props["target"].(string)
			if !ok || target == "" {
				return Command{}, fmt.Errorf("target must be a non-empty string")
			}
			return Command{
				Args:  []string{target},
				Flags: map[string]bool{},
			}, nil
		},
		Function: cmdWhere,
	},
}

Commands lists all the commands available in the cli.

Functions

func Run

func Run(flags tools.CommonFlags)

Run runs a simple CLI-based stdin-stdout server to allow us to explore the code.

func WriteErr

func WriteErr(tt *term.Terminal, format string, a ...any)

WriteErr writes a formatted error message to the terminal in red color. The message is automatically terminated with a newline and the color formatting is reset after the message.

Parameters:

  • tt: Terminal to write to
  • format: Printf-style format string
  • a: Arguments for the format string

This is a convenience function for writing error messages with consistent red coloring in terminal environments.

func WriteSuccess

func WriteSuccess(tt *term.Terminal, format string, a ...any)

WriteSuccess writes a formatted success message to the terminal in green color. The message is automatically terminated with a newline and the color formatting is reset after the message.

Parameters:

  • tt: Terminal to write to
  • format: Printf-style format string
  • a: Arguments for the format string

This is a convenience function for writing success messages with consistent green coloring in terminal environments.

Types

type Command

type Command struct {
	// Name is the name of the command (e.g. exit, ls, ...)
	Name string

	// Args contains all the non-named arguments (arguments without keys)
	Args []string

	// NamedArgs contains all the named arguments (arguments --key value)
	NamedArgs map[string]string

	// Flags contains all the flags (arguments -key)
	Flags map[string]bool
}

Command contains the parsed arguments and name of a command in the command-line tool

func ParseCommand

func ParseCommand(cmd string) Command

ParseCommand parses a command of the form "command arg1 arg2 -name1 namedArg1 -flag1 arg3"

  • the first string is the name of the command
  • every string preceded by -- is a named argument, and the next string will be parsed as its value A valid named argument MUST have a value.
  • every string preceded by - but not -- is a flag,
  • every other string will be a non named argument

type CommandDefinition added in v0.5.0

type CommandDefinition struct {
	// Name of the command
	Name string
	// Description of the command
	Description string
	// InputSchema of the command (for the MCP server)
	InputSchema tools.MCPInputSchema
	// SchemaTranslation is the translation from the input schema to the command schema
	SchemaTranslation func(props map[string]interface{}) (Command, error)
	// Function to run the command
	Function func(o Outputter, s *Session, command Command, withTest bool) bool
}

A CommandDefinition defines a command both for the cli and for the MCP server.

func (CommandDefinition) ToMCPToolDefinition added in v0.5.0

func (c CommandDefinition) ToMCPToolDefinition() tools.MCPTool

ToMCPToolDefinition returns the MCP tool definition of the command.

type NameAndLoc

type NameAndLoc struct {
	// contains filtered or unexported fields
}

NameAndLoc hold a name and location together

type Outputter added in v0.5.0

type Outputter struct {
	// contains filtered or unexported fields
}

Outputter provides a unified interface for writing output to either a terminal or standard writers. It automatically handles color formatting when writing to a terminal and falls back to plain text when writing to regular io.Writers.

The Outputter can operate in two modes:

  • Terminal mode: Uses a term.Terminal for colored output with escape sequences
  • Writer mode: Uses separate io.Writers for normal output and error output

This abstraction allows the same code to work in both interactive terminal environments and non-interactive contexts (like file output or pipes).

func NewOutputter added in v0.5.0

func NewOutputter(out io.Writer, err io.Writer) Outputter

NewOutputter creates a new Outputter that writes to the provided io.Writers. This constructor is used for non-interactive environments where colored output is not desired or supported.

Parameters:

  • out: Writer for normal output messages
  • err: Writer for error messages

Returns an Outputter configured for plain text output without terminal features.

func NewTerminalOutputter added in v0.5.0

func NewTerminalOutputter(tt *term.Terminal) Outputter

NewTerminalOutputter creates a new Outputter that writes to a terminal. This constructor is used for interactive environments where colored output and terminal escape sequences are supported.

Parameters:

  • tt: Terminal instance that supports colored output and escape sequences

Returns an Outputter configured for terminal output with color support.

func (Outputter) EscBlue added in v0.5.0

func (o Outputter) EscBlue() []byte

EscBlue returns the ANSI escape sequence for blue text color. When using a terminal, returns the terminal's blue escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) EscCyan added in v0.5.0

func (o Outputter) EscCyan() []byte

EscCyan returns the ANSI escape sequence for cyan text color. When using a terminal, returns the terminal's cyan escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) EscGreen added in v0.5.0

func (o Outputter) EscGreen() []byte

EscGreen returns the ANSI escape sequence for green text color. When using a terminal, returns the terminal's green escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) EscMagenta added in v0.5.0

func (o Outputter) EscMagenta() []byte

EscMagenta returns the ANSI escape sequence for magenta text color. When using a terminal, returns the terminal's magenta escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) EscRed added in v0.5.0

func (o Outputter) EscRed() []byte

EscRed returns the ANSI escape sequence for red text color. When using a terminal, returns the terminal's red escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) EscReset added in v0.5.0

func (o Outputter) EscReset() []byte

EscReset returns the ANSI escape sequence to reset text formatting. When using a terminal, returns the terminal's reset escape sequence. When using writers, returns an empty byte slice (no formatting reset needed).

This method should be used after applying color escape sequences to return text formatting to normal.

func (Outputter) EscWhite added in v0.5.0

func (o Outputter) EscWhite() []byte

EscWhite returns the ANSI escape sequence for white text color. When using a terminal, returns the terminal's white escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) EscYellow added in v0.5.0

func (o Outputter) EscYellow() []byte

EscYellow returns the ANSI escape sequence for yellow text color. When using a terminal, returns the terminal's yellow escape sequence. When using writers, returns an empty byte slice (no coloring).

This method allows manual control over text coloring in terminal mode.

func (Outputter) Write added in v0.5.0

func (o Outputter) Write(format string, a ...any)

Write writes formatted output to the appropriate output channel. This is the standard method for writing normal output messages. No special coloring is applied - use WriteErr or WriteSuccess for colored output.

Parameters:

  • format: Printf-style format string
  • a: Arguments for the format string

Unlike WriteErr and WriteSuccess, this method does not automatically add a newline.

func (Outputter) WriteErr added in v0.5.0

func (o Outputter) WriteErr(format string, a ...any)

WriteErr writes formatted error messages to the appropriate output channel. When using a terminal, the message is displayed in red color. When using writers, the message is written to the error writer.

Parameters:

  • format: Printf-style format string
  • a: Arguments for the format string

The message is automatically terminated with a newline when using terminal mode.

func (Outputter) WriteSuccess added in v0.5.0

func (o Outputter) WriteSuccess(format string, a ...any)

WriteSuccess writes formatted success messages to the appropriate output channel. When using a terminal, the message is displayed in green color. When using writers, the message is written to the normal output writer.

Parameters:

  • format: Printf-style format string
  • a: Arguments for the format string

The message is automatically terminated with a newline when using terminal mode.

func (Outputter) Writer added in v0.5.0

func (o Outputter) Writer() io.Writer

Writer returns the appropriate io.Writer for normal output. If the Outputter is configured with a terminal, it returns the terminal. Otherwise, it returns the configured output writer.

This method provides access to the underlying writer for cases where direct writing is needed without formatting.

type Session added in v0.5.0

type Session struct {
	// contains filtered or unexported fields
}

Session stores state information about the current cli Session

func NewSession added in v0.5.0

func NewSession(flags tools.CommonFlags, isTt bool) *Session

NewSession returns a new session with the provided flags.

func (*Session) LoadConfig added in v0.5.0

func (s *Session) LoadConfig(o Outputter, reload bool) result.Result[config.State]

LoadConfig triggers the config loading for the session. Does nothing when the config state is non-null and reload is not true.

func (*Session) Reset added in v0.5.0

func (s *Session) Reset()

Reset the session (clear all the program state fields)

Jump to

Keyboard shortcuts

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