class Analyzer::Haskell::Cli

Overview

Surfaces the command-line attack surface of Haskell programs as cli:// endpoints: optparse-applicative (long/argument/command), turtle (Turtle.Options: optText/optInt/optPath/switch/arg/argText) plus getEnv reads. Line-scan, merged by URL.

Defined in:

analyzer/analyzers/haskell/cli.cr

Constant Summary

ARGUMENT = /(?<![A-Za-z0-9_'])argument\b.*?\bmetavar\s+"([A-Za-z0-9][\w-]*)"/
COMMAND = /(?<![A-Za-z0-9_'])command\s+"([A-Za-z0-9][\w-]*)"/
GET_ENV = /(?<![A-Za-z0-9_'])(?:getEnv|lookupEnv)\s+"([A-Za-z0-9_]\w*)"/
LONG = /(?<![A-Za-z0-9_'])long\s+"([A-Za-z0-9][\w-]*)"/
MARKERS = /\bimport\s+(?:qualified\s+)?Options\.Applicative\b|\b(?:execParser|strOption|subparser|hsubparser)\b|\bimport\s+(?:qualified\s+)?System\.Console\.(?:GetOpt|CmdArgs)\b|\bgetArgs\b|#{TURTLE_MARKERS}/
TEST_PATH_RE = Regex.union("/test/", "_spec.")

Per-path test-file gate. A precompiled Regex.union (PCRE2 JIT) replaces the two OR-ed String#includes? scans it used to run -- provably equivalent since union auto-escapes each literal, and a single regex pass over lower is cheaper than two substring scans.

TURTLE_ARG = /(?<![A-Za-z0-9_'])arg(?:Text|Int|Integer|Double|Read|Path)\s+"([A-Za-z0-9][\w-]*)"/

argText plus the type-suffixed positional combinators, mirroring the TURTLE_OPT alternation above.

TURTLE_ARG_FN = /(?<![A-Za-z0-9_'])arg\s+(?:[A-Za-z_][\w.']*|\([^()]*\))\s+"([A-Za-z0-9][\w-]*)"\s+"[^"]*"/

arg (the generic reader-based combinator) is an even more common bare word than switch (ordinary local variables/parameters are routinely named arg). Real usage is always arg <reader> "name" "desc": the reader is an identifier/qualified-name or a parenthesized expression, and BOTH the name and description string literals are required -- a stray f arg x "str" call/comment only has one trailing string and won't match.

TURTLE_MARKERS = /\bimport\s+(?:qualified\s+)?Turtle\.Options\b|\bimport\s+(?:qualified\s+)?Turtle\b\s*\([^)]*\boptions\b[^)]*\)/

Turtle re-exports Turtle.Options from the top-level Turtle module, which is used pervasively for plain shell scripting too, so gate on either the qualified submodule import or an explicit options name in the unqualified import list, never a bare import Turtle.

TURTLE_OPT = /(?<![A-Za-z0-9_'])opt(?:Text|Int|Integer|Double|Path|Read|Bool)\s+"([A-Za-z0-9][\w-]*)"/

Turtle.Options: optText/optInt/optPath/... and switch take the flag name as their own first quoted argument (no long "..." wrapper), so these need dedicated regexes distinct from LONG above.

TURTLE_SWITCH = /(?<![A-Za-z0-9_'])switch\s+"([A-Za-z0-9][\w-]*)"\s+'.'\s+"[^"]*"/

switch is a bare, extremely common Haskell identifier (also used by optparse-applicative itself, and as a plain string-pattern dispatch function). Real Turtle usage is always the full 3-argument shape switch "name" 'c' "desc"; require the trailing short-char + description so we don't fire on unrelated switch "x" = ... equations.

WEB_RE = /\bimport\s+(?:qualified\s+)?(?:Web\.Scotty|Servant|Yesod|Network\.Wai)\b/

Instance Method Summary

Instance methods inherited from class Analyzer

analyze analyze, base_path : String base_path, base_paths : Array(String) base_paths, callees_needed? : Bool callees_needed?, 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, 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

Instance methods inherited from module FileHelper

all_files : Array(String) all_files, 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_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

Instance Method Detail

def analyze #

[View source]