class
Analyzer::Java::Cli
- Analyzer::Java::Cli
- Analyzer
- Reference
- Object
Overview
Surfaces the command-line attack surface of Java programs as cli://
endpoints: one endpoint per (sub)command with named options
(param_type "flag"), positional arguments ("argument") and consumed
environment variables ("env"). Covers picocli, args4j, JCommander,
commons-cli, airline and jopt-simple, plus gated System.getenv reads.
Line-scan analyzer (Go/Ruby/Rust CLI house style) merging endpoints by URL. Subclasses Analyzer directly (JavaEngine is a module) and uses JavaEngine.test_path? to skip tests.
Included Modules
Defined in:
analyzer/analyzers/java/cli.crConstant Summary
-
ADD_OPTION_LL =
/\.addOption\s*\(\s*"([^"]+)"\s*,\s*"([^"]+)"\s*,/ -
commons-cli (no subcommands; flags on root).
-
ADD_OPTION_SHORT =
/\.addOption\s*\(\s*"([^"]+)"\s*,\s*(?:true|false)\s*,/ -
Current (non-deprecated)
addOption(String opt, boolean hasArg, String description)overload used for short-only flags. Thetrue|falseliteral gate in the 2nd argument position guarantees this never overlaps with ADD_OPTION_LL above (which requires a quoted string there), so a given call is matched by exactly one of the two. -
ARGUMENT_ATTR =
/@Argument\b/ -
ARGUMENTS_ATTR =
/@Arguments\s*\(([^)]*)\)/ -
CLI_GATE_RE =
/@Command\b|@Parameter\b|new\s+JCommander|new\s+CmdLineParser|new\s+Options\s*\(/ -
COMMAND_ATTR =
/@Command\s*\([^)]*\bname\s*=\s*"([^"]+)"/ -
COMMAND_NAME_KV =
/\bname\s*=\s*"([^"]+)"/ -
COMMAND_OPEN =
/@Command\b/ -
FIELD_DECL =
/^\s*(?:public|private|protected)?\s*(?:final\s+|static\s+)*[\w<>\[\].]+\s+(\w+)\s*[;=]/ -
GET_ENV =
/\bSystem\.getenv\s*\(\s*"([^"]+)"\s*\)/ -
JC_COMMAND =
/@Parameters\s*\([^)]*\bcommandNames\s*=\s*\{?\s*"([^"]+)"/ -
JOPT_ACCEPTS =
/\b(\w+)\.accepts\s*\(\s*"([^"]+)"/ -
JOPT_ACCEPTS_ALL =
/\b(\w+)\.acceptsAll\s*\(\s*(?:Arrays\.asList|List\.of|Collections\.singletonList)\s*\(\s*"([^"]+)"/ -
JOPT_PARSER_DECL =
/\b(\w+)\s*=\s*new\s+OptionParser\s*\(/ -
jopt-simple (no subcommands; flags land on root, like commons-cli).
.accepts("flag")/.acceptsAll(List.of("f","flag"))are only real CLI flags when called on a variable that was actually bound tonew OptionParser(...)— jopt-simple'sOptionParserisn't the only class with anaccepts(...)method (e.g. aFormatMatcher.accepts(fmt)helper), so the receiver is tracked per-file in the same forward pass and matches on an untracked receiver are dropped rather than attributed to the root command. -
LIB_MARKERS =
["picocli.", "org.kohsuke.args4j", "com.beust.jcommander", "org.apache.commons.cli", "com.github.rvesse.airline", "io.airlift.airline", "joptsimple."] -
LIB_MARKERS_RE =
Regex.union(LIB_MARKERS) -
LONG_OPT =
/\.longOpt\s*\(\s*"([^"]+)"/ -
OPTION_ATTR =
/@Option\s*\(([^)]*)\)/ -
PARAMETER_ATTR =
/@Parameter\s*\(([^)]*)\)/ -
PARAMETERS_ATTR =
/@Parameters\b([^)\n]*\)?)/ -
RAW_CLI_MARKERS_RE =
Regex.union(LIB_MARKERS + ["@Command", "@Parameter", "JCommander", "CmdLineParser", "Options"]) -
Cheap pre-gate applied to the RAW file, before the comment strip.
strip_commentsmaterialises anArray(Char)of the whole file and rebuilds it character by character; running it on all 8,658.javafiles in spring-boot to then reject 97% of them on the gate below was the bulk of this analyzer. Stripping only replaces characters with' 'or'\n'and never changes the character count, so a literal with no whitespace in it can appear in the stripped text only if it already appears, unchanged, in the raw text. Each alternative of the real gate requires one of these literals verbatim (thenew\s+Xforms requireX; the\s-carrying parts are deliberately not relied on, since blanking a comment can manufacture whitespace). Files that pass still go through the full, comment-aware gate. -
SUBCOMMANDS_KEY =
/\bsubcommands\s*=/ -
WEB_FRAMEWORK_RE =
/\bimport\s+(?:org\.springframework|jakarta\.ws\.rs|javax\.ws\.rs|io\.quarkus|io\.micronaut|io\.javalin|io\.vertx|com\.linecorp\.armeria|io\.dropwizard|spark\.|org\.apache\.struts)/
Class Method Summary
Instance Method Summary
- #analyze
-
#tech : String
Instance-side view of the same declaration.
Instance methods inherited from class Analyzer
analyze
analyze,
base_path : String
base_path,
base_paths : Array(String)
base_paths,
base_relative_path(path : String) : String
base_relative_path,
callees_needed? : Bool
callees_needed?,
content_matches?(content : String, markers : Regex) : Bool
content_matches?,
http_header_name(name : String) : String | Nil
http_header_name,
line_number_for_index(content : String, char_index : Int32) : Int32
line_number_for_index,
logger : NoirLogger
logger,
parallel_analyze(files : Array(String), &block : String -> Nil)
parallel_analyze,
read_file_content(path : String) : String
read_file_content,
result : Array(Endpoint)
result,
tech : String
tech,
unique_params(params : Array(Param)) : Array(Param)
unique_params,
url : String
url,
web_root_path(path : String, markers : Array(String)) : String
web_root_path
Constructor methods inherited from class Analyzer
new(options : Hash(String, YAML::Any))
new
Macros inherited from class Analyzer
analyzer_for(tech)
analyzer_for
Instance methods inherited from module FileHelper
all_files : Array(String)
all_files,
get_files_by_basename(basename : String) : Array(String)
get_files_by_basename,
get_files_by_extension(extension : String) : Array(String)
get_files_by_extension,
get_files_by_extensions(extensions : Array(String)) : Array(String)
get_files_by_extensions,
get_files_by_prefix(prefix : String) : Array(String)
get_files_by_prefix,
get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String)
get_files_by_prefix_and_extension,
get_files_by_relative_path(relative_path : String, root : String = "") : Array(String)
get_files_by_relative_path,
get_public_dir_files(base_path : String, folder : String) : Array(String)
get_public_dir_files,
get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String)
get_public_files,
walked_path(expanded : String) : String
walked_path
Class Method Detail
Instance Method Detail
Instance-side view of the same declaration. The per-file rescues live on
this base class, which has no way to name the analyzer that is running
inside them, so a skipped file could not be attributed to a tech.
Deriving it from analyzer_for keeps the name written exactly once.