Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"run_web_shot_caption": "Dashboard — optional tenant/technician fields and Start scan (no shell required).",
"run_auto_h3": "For automation / scripting",
"run_cli_h3": "From the CLI",
"run_cli_p1": "From the repo root, prefer uv run python main.py (or activate your venv and run python main.py). Full flag list: python main.py --help. Packaged installs: man 1 data-boar (command) and man 5 data-boar (config file format).",
"run_cli_p1": "Run data-boar from any terminal on your PATH. Full flag list: data-boar --help. Reference: man 1 data-boar (command) and man 5 data-boar (config file format).",
"run_cli_oneshot": "One-shot audit (same targets as a dashboard scan):",
"run_cli_api": "Start the API / dashBOARd (no scan until you use the browser or HTTP):",
"run_cli_tls": "You must either terminate TLS at the process (--https-cert-file + --https-key-file, or the same under api: in config) or explicitly accept plaintext with --allow-insecure-http (or api.allow_insecure_http: true). The official Docker image passes --allow-insecure-http by default; mount certs and remove it for HTTPS.",
Expand Down
2 changes: 1 addition & 1 deletion api/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"run_web_shot_caption": "Painel — campos opcionais de cliente/técnico e Iniciar varredura (sem shell).",
"run_auto_h3": "Para automação / scripting",
"run_cli_h3": "Pela CLI",
"run_cli_p1": "Na raiz do repositório, prefira uv run python main.py (ou ative o venv e execute python main.py). Lista completa de flags: python main.py --help. Instalações empacotadas: man 1 data-boar (comando) e man 5 data-boar (formato do arquivo de configuração).",
"run_cli_p1": "Execute data-boar em qualquer terminal no PATH. Lista completa de flags: data-boar --help. Referência: man 1 data-boar (comando) e man 5 data-boar (formato do arquivo de configuração).",
"run_cli_oneshot": "Auditoria pontual (mesmos alvos que uma varredura pelo painel):",
"run_cli_api": "Subir a API / dashBOARd (sem varredura até usar o navegador ou HTTP):",
"run_cli_tls": "É preciso terminar TLS no processo (--https-cert-file + --https-key-file, ou o mesmo em api: na config) ou aceitar explicitamente texto claro com --allow-insecure-http (ou api.allow_insecure_http: true). A imagem Docker oficial passa --allow-insecure-http por padrão; monte certificados e remova para HTTPS.",
Expand Down
6 changes: 5 additions & 1 deletion api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1686,11 +1686,15 @@ def run_one_target():
@app.get("/{locale_slug}/help", response_class=HTMLResponse)
async def help_page(request: Request, locale_slug: LocaleSlug):
"""Help and documentation page: quickstart, config example, links to README/USAGE docs."""
from utils.cli_presentation import cli_help_context

tag = _locale_tag_from_slug(locale_slug.value)
return templates.TemplateResponse(
request=request,
name="help.html",
context=_i18n_template_context(request, locale_slug.value, tag, {}),
context=_i18n_template_context(
request, locale_slug.value, tag, cli_help_context()
),
)


Expand Down
27 changes: 13 additions & 14 deletions api/templates/help.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,23 @@ <h3>{{ t('help.run_web_h3') }}</h3>
<h3>{{ t('help.run_auto_h3') }}</h3>
<p class="muted">{{ t('help.run_cli_p1') }}</p>
<p><strong>{{ t('help.run_cli_oneshot') }}</strong></p>
<pre><code>uv run python main.py --demo
data-boar --demo
uv run python main.py --config config.yaml
uv run python main.py --config config.yaml --validate-config
uv run python main.py --config config.yaml --diff &lt;session_a&gt; &lt;session_b&gt;
uv run python main.py --config config.yaml --diff &lt;session_a&gt; &lt;session_b&gt; --fail-on-new-high
uv run python main.py --config config.yaml --export-dsar &lt;session_id&gt;
uv run python main.py --config config.yaml --export-dsar &lt;session_id&gt; --dsar-output dsar.json
uv run python main.py --config config.yaml --tenant "Acme Corp" --technician "Alice Colleague-V"
<pre><code>data-boar --demo
data-boar --config config.yaml
data-boar --config config.yaml --validate-config
data-boar --config config.yaml --diff &lt;session_a&gt; &lt;session_b&gt;
data-boar --config config.yaml --diff &lt;session_a&gt; &lt;session_b&gt; --fail-on-new-high
data-boar --config config.yaml --export-dsar &lt;session_id&gt;
data-boar --config config.yaml --export-dsar &lt;session_id&gt; --dsar-output dsar.json
data-boar --config config.yaml --tenant "Acme Corp" --technician "Alice Colleague-V"
# Same optional toggles as the dashboard checkboxes (this run only):
uv run python main.py --config config.yaml --scan-compressed --content-type-check --scan-stego --jurisdiction-hint</code></pre>
data-boar --config config.yaml --scan-compressed --content-type-check --scan-stego --jurisdiction-hint</code></pre>
<p><strong>{{ t('help.run_cli_api') }}</strong></p>
<p class="muted">{{ t('help.run_cli_tls') }}</p>
<pre><code>uv run python main.py --config config.yaml --web --https-cert-file server.crt --https-key-file server.key
<pre><code>data-boar --config config.yaml --web --https-cert-file server.crt --https-key-file server.key
# Lab / loopback only — explicit plaintext:
uv run python main.py --config config.yaml --web --allow-insecure-http
data-boar --config config.yaml --web --allow-insecure-http
# Default bind is loopback (127.0.0.1). Listen on all interfaces only with network controls:
uv run python main.py --config config.yaml --web --allow-insecure-http --host 0.0.0.0 --port 8088</code></pre>
data-boar --config config.yaml --web --allow-insecure-http --host 0.0.0.0 --port 8088</code></pre>
<p class="muted">{{ t('help.run_cli_bind') }}</p>
<p><strong>{{ t('help.run_cli_danger') }}</strong></p>
<p><strong>{{ t('help.run_cli_audit') }}</strong></p>
Expand Down Expand Up @@ -118,7 +117,7 @@ <h2>{{ t('help.cfg_h2') }}</h2>

- name: "Documentos_LGPD"
type: filesystem
path: /home/user/Documents/LGPD
path: {{ filesystem_lgpd_example }}
recursive: true

file_scan:
Expand Down
18 changes: 8 additions & 10 deletions docs/data_boar.1
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ is accepted as an alias; both names refer to the same application.
The actual entry point is:
.br
.RS 4
.B python main.py
.B data-boar
[
.I OPTIONS
]
Expand Down Expand Up @@ -563,19 +563,19 @@ The "Start scan" button triggers
.TP
Minimal scan with default config:
.RS
.B python main.py \-\-config config.yaml
.B data-boar \-\-config config.yaml
.RE
.
.TP
Scan with tenant and technician metadata:
.RS
.B python main.py \-\-config config.yaml \-\-tenant "Acme Corp" \-\-technician "Alice Colleague-V"
.B data-boar \-\-config config.yaml \-\-tenant "Acme Corp" \-\-technician "Alice Colleague-V"
.RE
.
.TP
Wipe all sessions, findings and generated reports (dangerous):
.RS
.B python main.py \-\-config config.yaml \-\-reset\-data
.B data-boar \-\-config config.yaml \-\-reset\-data
.RE
.
.SS Web API
Expand All @@ -589,35 +589,33 @@ in the configuration) for plaintext HTTP.
Zero\-config demo (no configuration file):
.RS
.B data-boar \-\-demo
.br
.B python main.py \-\-demo
.RE
.
.TP
Start the API on port 8088 (plaintext, explicit opt\-in):
.RS
.B python main.py \-\-config config.yaml \-\-web \-\-allow\-insecure\-http \-\-port 8088
.B data-boar \-\-config config.yaml \-\-web \-\-allow\-insecure\-http \-\-port 8088
.RE
.
.TP
HTTPS (PEM cert + key; TLS >= 1.2):
.RS
.B python main.py \-\-config config.yaml \-\-web \-\-https\-cert\-file server.crt \-\-https\-key\-file server.key
.B data-boar \-\-config config.yaml \-\-web \-\-https\-cert\-file server.crt \-\-https\-key\-file server.key
.RE
.
.TP
Use CONFIG_PATH and start the server:
.RS
.nf
export CONFIG_PATH=/etc/lgpd\-audit/config.yaml
python main.py \-\-web \-\-allow\-insecure\-http \-\-port 8088
data-boar \-\-web \-\-allow\-insecure\-http \-\-port 8088
.fi
.RE
.
.TP
Listen on all interfaces (e.g.\& LAN or container without Docker env); use only with network controls:
.RS
.B python main.py \-\-config config.yaml \-\-web \-\-allow\-insecure\-http \-\-host 0.0.0.0 \-\-port 8088
.B data-boar \-\-config config.yaml \-\-web \-\-allow\-insecure\-http \-\-host 0.0.0.0 \-\-port 8088
.RE
.
.SS curl \- API examples
Expand Down
4 changes: 2 additions & 2 deletions docs/data_boar.5
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ default resolution when unset is
unless
.B API_HOST
is set or
.B "main.py \-\-web \-\-host"
.B "data-boar \-\-web \-\-host"
overrides);
optionally
.I require_api_key
Expand Down Expand Up @@ -480,7 +480,7 @@ Example main config (excerpt):
\fBapi:\fR
port: 8088
# host: 127.0.0.1 # optional bind address; see man 1 data-boar for CLI and API_HOST
# https_cert_file: /path/server.crt # optional: TLS for python main.py --web
# https_cert_file: /path/server.crt # optional: TLS for data-boar --web
# https_key_file: /path/server.key # optional: must pair with https_cert_file
# allow_insecure_http: false # explicit plaintext only when true (Docker image CMD often sets CLI flag instead)

Expand Down
53 changes: 5 additions & 48 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,62 +263,19 @@ def _run_session_diff_cli(


def main() -> None:
from utils.cli_presentation import build_cli_epilog, cli_prog_name

_cli_prog = cli_prog_name()
parser = argparse.ArgumentParser(
prog=_cli_prog,
description=(
"Data Boar — enterprise data discovery and risk governance engine. "
"Loads YAML/JSON config, scans configured databases/filesystems/APIs/shares, "
"stores finding metadata in local SQLite, and generates Excel reports with heatmaps. "
"Run once from the CLI or start a REST API dashboard (LGPD/GDPR/CCPA-aware patterns; "
"additional frameworks via config)."
),
epilog=(
"Configuration:\n"
" - Main config file (YAML or JSON) defines targets (databases, filesystems, APIs, shares),\n"
" detection options and report settings. Default is 'config.yaml' in the current directory.\n"
" - See docs/USAGE.md for a full schema and examples.\n"
"\n"
"CLI examples:\n"
" # One-shot audit with the default config.yaml\n"
" python main.py --config config.yaml\n"
"\n"
" # One-shot audit tagging tenant/customer and technician/operator\n"
' python main.py --config config.yaml --tenant "ACME Corp" --technician "Alice"\n'
"\n"
" # One-shot with archive scan + content-type detection (this run only)\n"
" python main.py --config config.yaml --scan-compressed --content-type-check\n"
"\n"
" # Validate config only (loader checks; no scan or API startup)\n"
" python main.py --config config.yaml --validate-config\n"
"\n"
" # Compare two scan sessions (CI: add --fail-on-new-high)\n"
" python main.py --config config.yaml --diff <session_a> <session_b>\n"
"\n"
" # DSAR-oriented JSON export for one session (stdout or --dsar-output)\n"
" python main.py --config config.yaml --export-dsar <session_id>\n"
"\n"
" # Wipe all collected data and generated reports (dangerous, see SECURITY.md)\n"
" python main.py --config config.yaml --reset-data\n"
"\n"
"Web/API examples:\n"
" # HTTPS: PEM cert + key (TLS >= 1.2)\n"
" python main.py --config config.yaml --web --https-cert-file server.crt --https-key-file server.key\n"
"\n"
" # Plaintext HTTP (explicit risk acceptance; required when not using TLS)\n"
" python main.py --config config.yaml --web --allow-insecure-http\n"
"\n"
" # Explicit port or bind (same flags as before, still need TLS or --allow-insecure-http)\n"
" python main.py --config config.yaml --web --allow-insecure-http --port 9090\n"
" python main.py --config config.yaml --web --allow-insecure-http --host 0.0.0.0\n"
"\n"
" # Zero-config demo (synthetic corpus, loopback dashboard — no config.yaml)\n"
" python main.py --demo\n"
" data-boar --demo\n"
"\n"
"Once a one-shot scan finishes, an Excel report and heatmap PNG are written under\n"
"the configured report.output_dir (default: current directory). When the API is\n"
"running, you can navigate to the documented endpoints (see README.md) to trigger\n"
"scans, list sessions and download the latest reports through the browser."
),
epilog=build_cli_epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
Expand Down
10 changes: 4 additions & 6 deletions scripts/build_locale_catalogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,8 @@ def _en() -> dict:
"run_auto_h3": "For automation / scripting",
"run_cli_h3": "From the CLI",
"run_cli_p1": (
"From the repo root, prefer uv run python main.py (or activate your venv and run "
"python main.py). Full flag list: python main.py --help. Packaged installs: "
"man 1 data-boar (command) and man 5 data-boar (config file format)."
"Run data-boar from any terminal on your PATH. Full flag list: data-boar --help. "
"Reference: man 1 data-boar (command) and man 5 data-boar (config file format)."
),
"run_cli_oneshot": "One-shot audit (same targets as a dashboard scan):",
"run_cli_api": "Start the API / dashBOARd (no scan until you use the browser or HTTP):",
Expand Down Expand Up @@ -578,9 +577,8 @@ def tr(d: dict) -> dict:
base["help"]["run_auto_h3"] = "Para automação / scripting"
base["help"]["run_cli_h3"] = "Pela CLI"
base["help"]["run_cli_p1"] = (
"Na raiz do repositório, prefira uv run python main.py (ou ative o venv e execute "
"python main.py). Lista completa de flags: python main.py --help. Instalações empacotadas: "
"man 1 data-boar (comando) e man 5 data-boar (formato do arquivo de configuração)."
"Execute data-boar em qualquer terminal no PATH. Lista completa de flags: data-boar --help. "
"Referência: man 1 data-boar (comando) e man 5 data-boar (formato do arquivo de configuração)."
)
base["help"]["run_cli_oneshot"] = (
"Auditoria pontual (mesmos alvos que uma varredura pelo painel):"
Expand Down
49 changes: 45 additions & 4 deletions tests/operator_help_sync_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,59 @@
operators should discover from `--help` or `/help`, add or adjust a row below and
update the surfaces listed in `docs/OPERATOR_HELP_AUDIT.md`.

Use ``None`` for a surface that intentionally does not carry that cue (e.g. `uv run`
is documented on `/help` and README, not in argparse text).
Use ``None`` for a surface that intentionally does not carry that cue (e.g. dev-only
``uv run`` flows live in repo docs, not in dashBOARd ``/help`` or installed ``--help``).

Canonical detail stays in docs/USAGE*.md, man pages, and the repo README — this
file only pins a minimal substring set so tests fail if copy disappears.
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from pathlib import Path

MAN_OPERATOR_PAGE_REL_PATHS: tuple[str, ...] = (
"docs/data_boar.1",
"docs/data_boar.5",
)

# Installed-user man pages: bold (.B) command examples must use data-boar, not dev entrypoints.
_MAN_B_LINE_DEV_INVOCATION = re.compile(
r"^\s*\.B\b.*(?:\buv run\b|python main\.py|\bmain\.py\s+\\-)",
re.IGNORECASE,
)


def collect_man_operator_dev_invocation_violations(
repo_root: Path | None = None,
) -> list[str]:
"""Return violations if operator man troff sources show dev-only CLI invocations."""
root = repo_root or Path(__file__).resolve().parents[1]
violations: list[str] = []
for rel in MAN_OPERATOR_PAGE_REL_PATHS:
path = root / rel
if not path.is_file():
violations.append(f"{rel}: missing man page source")
continue
for lineno, line in enumerate(
path.read_text(encoding="utf-8").splitlines(), start=1
):
stripped = line.strip()
if stripped.startswith(".BR python (1)"):
continue
if "uv run" in line:
violations.append(f"{rel}:{lineno}: uv run in operator man page")
if "python main.py" in line:
violations.append(
f"{rel}:{lineno}: python main.py in operator man page"
)
if _MAN_B_LINE_DEV_INVOCATION.search(line):
violations.append(
f"{rel}:{lineno}: dev invocation in .B example line: {stripped!r}"
)
return violations


@dataclass(frozen=True)
Expand Down Expand Up @@ -158,8 +201,6 @@ class OperatorHelpMarker:
"--jurisdiction-hint",
_MAN_JURISDICTION_HINT,
),
# Web /help recommends uv; full docs in README — not duplicated in argparse.
OperatorHelpMarker("uv_run", None, "uv run", None),
OperatorHelpMarker("api_host_env", "API_HOST", "API_HOST", "API_HOST"),
OperatorHelpMarker("bind_loopback", "127.0.0.1", "127.0.0.1", "127.0.0.1"),
OperatorHelpMarker(
Expand Down
Loading