Skip to content

Latest commit

History

15 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

commonhuman-cli

PythonPyPILicenseZero deps

Shared CLI/terminal UX primitives for CommonHuman-Lab tools — colour, logging, output formatting, interactive prompts, and scan result infrastructure. One place. No duplication.

pip install commonhuman-cli

Why it exists

The CommonHuman-Lab toolkit is built around a consistent operator experience — every tool speaks the same visual language, responds to the same flags, and produces output you can pipe without surprises.

commonhuman-cli is the single source of truth for that experience. Tools that use it get:

  • Instant consistency — colour conventions, log prefixes, and summary layout are shared, not agreed upon.
  • Zero boilerplate — interactive wizard mode, URL list loading, header parsing, and exclude-pattern compilation are one import away.
  • Correct behaviour from day one — lazy TTY detection, thread-safe scan results, and namespace-isolated logging come built in.
  • A single place to improve — a fix or a new UX pattern lands in every tool at once.

Quick start

fromcommonhuman_cli.outputimportsuccess, warning, errorfromcommonhuman_cli.loggingimportsetup_logging, get_loggerfromcommonhuman_cli.reporterimportScanResultBasefromcommonhuman_cli.promptsimportprompt, prompt_bool, sectionfromcommonhuman_cli.entrypointimportload_url_list, parse_headers, parse_auth_credfromcommonhuman_cli.report_htmlimportrender_htmlfromcommonhuman_cli.report_sarifimportrender_sarif

What's in it

ModulePurpose
commonhuman_cli.colourANSI colour functions, lazy TTY detection, banner rendering
commonhuman_cli.loggingFINDING custom level, StingLogger, coloured handler, setup_logging()
commonhuman_cli.outputStatus printers, summary block helpers, proof_url(), severity-tagged findings
commonhuman_cli.severitySeverity constants, ordering, colour mapping, and score helpers
commonhuman_cli.promptsInteractive prompt helpers for wizard-mode CLIs
commonhuman_cli.reporterScanResultBase dataclass — thread-safe, serialisable
commonhuman_cli.entrypointURL list loading, header parsing, exclude pattern compilation, auth-cred parsing
commonhuman_cli.report_htmlSelf-contained HTML report renderer (stdlib only)
commonhuman_cli.report_sarifSARIF 2.1.0 report renderer for CI / GitHub code scanning (stdlib only)

Modules

colour

Lazy TTY detection — evaluated at call time, not import time, so pipe redirections are always respected.

fromcommonhuman_cli.colourimportRED, GREEN, YELLOW, CYAN, BOLD, DIM, render_bannerprint(GREEN("[+] finding confirmed"))
print(RED("[!] critical"))
print(DIM("[*] scanning..."))
print(render_banner(BANNER)) # wraps your tool's ASCII art in CYAN

Colour is automatically stripped when stdout is not a TTY (files, pipes, CI).


logging

Custom FINDING level (25 — between INFO and WARNING), coloured handler, and namespaced setup so two tools can run in the same process without interfering.

fromcommonhuman_cli.loggingimportsetup_loggingas_base# In your tool's _cli/logging.py:defsetup_logging(verbose: bool, quiet: bool) ->None:
_base(verbose, quiet, logger_name="breachsql")
fromcommonhuman_cli.loggingimportget_loggerlog=get_logger("breachsql.scanner")
log.info("scanning %s", url)
log.finding("SQLi confirmed in param %s", param) # GREEN [+]log.warning("WAF detected") # YELLOW [!]log.debug("raw response: %s", body[:200]) # CYAN [~]
LevelPrefixColour
DEBUG[~]CYAN
INFO[*]DIM
FINDING (25)[+]GREEN
WARNING+[!]YELLOW

severity

Severity constants, ordering, and display helpers shared across all findings.

fromcommonhuman_cli.severityimportSeverity, severity_colour, severity_label, severity_scoreSeverity.CRITICAL# "critical"Severity.HIGH# "high"Severity.MEDIUM# "medium"Severity.LOW# "low"Severity.INFO# "info"severity_colour("critical") # REDseverity_label("high") # "HIGH"severity_score("medium") # 3 (0=info … 5=critical)

output

One-liner status printers and structured summary block helpers.

fromcommonhuman_cli.outputimport (
success, warning, error, info, debug,
print_header, print_footer, print_scan_meta,
print_finding, print_finding_severity, print_severity_summary,
print_errors, proof_url,
)
# Status linessuccess("3 findings confirmed")
warning("WAF detected — switching to evasion mode")
error("connection refused") # → stderr# Summary blockprint_header("BreachSQL — Scan Summary")
print_scan_meta(
target="https://target.com",
duration_s=4.2,
requests_sent=312,
crawled_urls=18,
params_tested=47,
waf_detected="Cloudflare",
**{"DBMS detected": "mysql"}, # tool-specific extra rows
)
# Finding blockfromcommonhuman_cli.colourimportREDprint_finding(
index=1,
tag="ERROR-BASED SQLi",
tag_colour_fn=RED,
fields=[
("Param", "id"),
("URL", "https://target.com/item?id=1"),
("DBMS", "mysql"),
("Payload", "' AND 1=1--"),
("Evidence", "You have an error in your SQL syntax"),
],
proof=proof_url("https://target.com/item?id=1", "id", "' AND 1=1--", append=True),
)
print_errors(result.errors)
print_footer()

For findings that carry a Severity value, use print_finding_severity and print_severity_summary instead of raw colour functions:

fromcommonhuman_cli.severityimportSeverityprint_finding_severity(
index=1,
tag="PROTOTYPE POLLUTION",
severity=Severity.HIGH,
fields=[("Source", "location.hash"), ("Sink", "_.merge()")],
proof="https://target.com/page#payload",
)
# End-of-scan summary rowprint_severity_summary({"critical": 0, "high": 2, "medium": 5, "low": 1, "info": 3})

proof_url()

Builds a percent-encoded PoC URL. The append flag controls injection style:

# SQLi style — appends payload to the original valueproof_url("https://t.com/s?id=1", "id", "' AND 1=1--", append=True)
# → https://t.com/s?id=1%27+AND+1%3D1--# XSS style — replaces the value entirelyproof_url("https://t.com/s?q=x", "q", "<script>alert(1)</script>", append=False)
# → https://t.com/s?q=%3Cscript%3Ealert%281%29%3C%2Fscript%3E# Returns "" on malformed or schemeless URLs — never throwsproof_url("not-a-url", "q", "payload") # → ""

prompts

Interactive wizard helpers — identical behaviour across all tools.

fromcommonhuman_cli.promptsimportprompt, prompt_bool, section, safe_intsection("Target")
url=prompt(" Target URL", hint="https://target.com/search?q=test")
section("Scan options")
level=safe_int(prompt(" Scan level", default="1"), default=1, lo=1, hi=3)
crawl=prompt_bool(" Enable crawler", default=False)

safe_int clamps to [lo, hi] and returns default on non-numeric input. prompt and prompt_bool both exit cleanly on Ctrl+C / EOF.


reporter

Base dataclass for scan results. Inherit from it and add tool-specific finding lists.

fromcommonhuman_cli.reporterimportScanResultBasefromdataclassesimportdataclass, field@dataclassclassScanResult(ScanResultBase):
# base provides: target, duration_s, waf_detected, stats, log, errors, _lockdbms_detected: str|None=Noneerror_based: list=field(default_factory=list)
boolean_based: list=field(default_factory=list)
@propertydeftotal_findings(self) ->int:
returnlen(self.error_based) +len(self.boolean_based)
defto_dict(self) ->dict:
d=self._base_dict() # common fields pre-populatedd["dbms_detected"] =self.dbms_detectedd["total_findings"] =self.total_findingsreturnd# Usageresult=ScanResult(target="https://target.com")
result.requests_sent+=1result.append_error("connection timeout") # thread-saferesult.finish() # sets duration_s

All _append() calls are protected by a threading.Lock — safe for multi-threaded scanners.


entrypoint

Boilerplate that every __main__.py needs, extracted once.

fromcommonhuman_cli.entrypointimport (
load_url_list, compile_exclude_patterns, parse_headers, validate_timeout,
parse_auth_cred,
)
urls=load_url_list(args.url_list) # skips blanks and # comments, exit(2) on IOErrorpatterns=compile_exclude_patterns(args.exclude) # exit(2) on bad regexheaders=parse_headers(args.header) # ["Key:Val"] → {"Key": "Val"}validate_timeout(args.timeout) # warns to stderr if below minimum# Split "user:pass" (or "user:pa:ss") into (username, password)username, password=parse_auth_cred("admin:s3cr3t")

parse_auth_cred splits on the first :, so passwords that contain colons work correctly. Raises ValueError if the string is blank or contains no colon.


report_html

Renders a self-contained HTML report from one or more scan result dicts. No external dependencies — the entire page (styles included) is emitted as a single string.

fromcommonhuman_cli.report_htmlimportrender_htmlhtml=render_html(
results=[result.to_dict()], # list of ScanResult.to_dict() outputstool_name="StingXSS",
tool_version="0.1.6",
)
withopen("report.html", "w", encoding="utf-8") asfh:
fh.write(html)

Each finding is rendered with a severity badge and a collapsible detail table. Unknown/missing severities fall back to a neutral grey. HTML-special characters in finding values are always escaped.


report_sarif

Renders a SARIF 2.1.0 document from one or more scan result dicts. Compatible with GitHub code scanning, VS Code SARIF Viewer, and any SAST tooling that accepts SARIF.

fromcommonhuman_cli.report_sarifimportrender_sarifimportjsonRULES= {
"reflected_xss": ("Reflected XSS", "XSS payload reflected in HTTP response"),
"dom_xss": ("DOM XSS", "Tainted data flows from a source to a sink"),
# one entry per finding type your tool emits
}
sarif=render_sarif(
results=[result.to_dict() forresultinall_results],
tool_name="StingXSS",
tool_version="0.1.6",
rules=RULES,
)
withopen("results.sarif", "w", encoding="utf-8") asfh:
json.dump(sarif, fh, indent=2)

Severity mapping: critical/higherror, mediumwarning, lownote, infonone. Unknown severities default to warning. Finding URL is resolved from url → inject_url → endpoint → "".


Design principles

  • Zero runtime dependencies — stdlib only. Tools keep their own deps (requests, selenium, etc.).
  • No framework — plain functions and one dataclass. Nothing to learn, nothing to fight.
  • Lazy TTY detection — colour is checked at print time, not import time. Pipes and redirects always work.
  • Namespace isolationsetup_logging(logger_name="yourtool") scopes all logging so two tools coexist in the same process.
  • Composition over inheritanceScanResultBase gives you the shared fields; your ScanResult adds finding lists. No abstract base classes.

Tests

git clone https://github.com/CommonHuman-Lab/commonhuman-cli.git
cd commonhuman-cli
python -m venv .venv &&source .venv/bin/activate
pip install -e ".[dev]"
pytest
pytest tests/unit/ # isolated unit tests only
pytest tests/regression/ # behavioural contracts from migrated tools

📜 License

Licensed under the AGPLv3. You are free to use, modify, and distribute this software. If you run it as a service or distribute it, the source must remain open.

For commercial licensing, contact the author.

About

Shared CLI/terminal UX primitives for CommonHuman-Lab tools — colour, logging, output formatting, interactive prompts, and scan result infrastructure. One place. No duplication.

Resources

Stars

2 stars

Watchers

2 watching

Forks

Contributors

Languages