Metadata-Version: 2.5
Name: hardn-cli
Version: 1.6.0
Summary: Command-line client for Hardn (hardn.si): run read-only security scans of sites you own from the terminal or CI.
Project-URL: Homepage, https://hardn.si
Project-URL: Documentation, https://hardn.si/developers#cli
Author-email: Hardn <support@hardn.si>
License: Proprietary
Keywords: ci,devsecops,hardn,scanner,security
Classifier: Environment :: Console
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# hardn-cli

Run Hardn's read-only security scans of sites you own from your terminal or CI, and fail the build when a scan
finds problems at or above the severity you choose. Python 3.9+, no dependencies.

## Install

```bash
pip install hardn-cli        # or: pipx install hardn-cli
hardn --version
```

## Use

Create an API key in Hardn (API keys → New key) and keep it as a secret.

```bash
export HARDN_API_KEY=hk_live_…
hardn targets                                   # your sites and their ids (tgt_…)
hardn scan --target tgt_… --fail-on high        # start a scan, wait, exit 1 on high or critical issues
hardn scan --target tgt_… --sarif hardn.sarif   # also write a SARIF report (GitHub code scanning)
hardn verify --target tgt_…                     # re-check ownership verification
hardn mcp                                       # Hardn's tools for a coding agent (see below)
hardn retest iss_…                              # re-run one issue's check after you fixed it, and wait
```

`hardn retest <issue id>` re-runs only the check that found that issue against the live site (free; 5 an issue and
30 an organisation in 24 hours) and prints the result: exit `0` fixed (verified), `1` still open, `2` not checked
(the reason is printed) or an error. `--no-wait` starts it and exits; `--timeout <seconds>` (default 1800 for a
public retest, 4200 for a deep one: like `hardn scan`, its own limit plus 10 minutes, since a retest waits in the
queue like any scan).

Options: `--fail-on none|critical|high|medium|low|info` (default `high`; checked before the scan starts),
`--type public|deep`, `--timeout <seconds>` (default 1800 for a public scan, 4200 for a deep one: the scan's own
limit of 20 or 60 minutes, plus 10 for the queue and the report), `--allow-partial` or `--strict` (below),
`--format text|json`, `--quiet`, `--api-url` (or `HARDN_API_URL`; default `https://api.hardn.si`).

The gate counts every finding of this scan at or above `--fail-on` that validation didn't reject: confirmed,
pending and inconclusive findings all count. Findings marked as false positives don't: the report lists them apart,
under `suppressed_findings`.

A scan can finish without testing the whole site, and then it never passes as if it had: the scan's own summary is
printed and the exit code is `2` when the site's bot protection answered instead of the site, the site's pages
couldn't be read, no check ran, or a deep scan's test accounts didn't sign in. Two partial scans did read the site
and only warn, so the findings decide: one that stopped at its request budget (a large site) and one that stopped
early because the site kept erroring or slowing down. `--strict` makes those exit `2` too; `--allow-partial` lets any
partial scan pass on its findings, with the warning. Findings at or above `--fail-on` exit `1` first, whatever the
scan covered.

Exit codes: `0` passed, `1` issues at or above `--fail-on` (for `verify`: not verified), `2` the scan didn't
complete, didn't test the whole site (above) or timed out, or a usage or API error. A network failure, a 429 or a
502–504 (an API release) while it waits for the scan or fetches its report is asked again, up to five times with a
growing pause, before it counts.

## In GitHub Actions

```yaml
- name: Hardn scan
  env:
    HARDN_API_KEY: ${{ secrets.HARDN_API_KEY }}
  run: |
    pip install hardn-cli
    hardn scan --target tgt_… --fail-on high --sarif hardn.sarif
```

## Use it from a coding agent (MCP)

`hardn mcp` lets a coding agent (Claude Code, Cursor, VS Code Copilot or any other MCP client) work with Hardn while
it works on your code: find what Hardn found, read the fix, fix it, then ask Hardn to check again. The agent's client
starts `hardn mcp` itself and talks to it over stdin and stdout. It uses the same API key and the same `/v1` calls as
the commands above, so it sees only your organisation, and Hardn still checks ownership, your plan and credits.

The tools it gives the agent:

- `list_sites`: your sites, their ids, whether each is verified, its grade and open issues.
- `start_scan`: start a public or deep scan of a verified site (by id or host) and get the scan id.
- `get_scan`: a scan's progress, and when it has finished its grade, score and counts.
- `list_issues`: open (or fixed, accepted, all) issues, most severe first, by site and lowest severity.
- `get_issue`: one issue with where Hardn saw it, the fix steps, Hardn's fix report and a prompt for the agent;
  what Hardn saw on the site (the finding's description and evidence) comes inside the prompt, marked as untrusted.
- `retest_issue`: after the fix is deployed, check that one issue again; follow the retest with `get_scan`.

It needs `hardn-cli` installed, with `hardn` on your `PATH` (if your editor can't find it, use the full path that
`which hardn` prints):

```bash
pipx install hardn-cli        # or: pip install hardn-cli
```

Use an API key from Hardn → API keys. The key belongs to one organisation: anyone who can use the agent can start
scans with it, and a scan the agent starts is paid like any other (a deep scan or a run over your plan's allowance
uses credits). Retests are free (5 an issue and 30 an organisation in any 24 hours). `start_scan` and `retest_issue` are marked as tools that do something, so your client asks
before it runs them; the other four only read.

**Claude Code**

```bash
claude mcp add hardn -e HARDN_API_KEY=hk_live_… -- hardn mcp
```

That adds it for you in this project (`--scope local`, the default). Add `--scope user` to have it in every project.
With `--scope project` it is written to `.mcp.json` in the repository for your whole team: keep the key out of that
file and let each person's own environment supply it:

```json
{
  "mcpServers": {
    "hardn": {
      "command": "hardn",
      "args": ["mcp"],
      "env": { "HARDN_API_KEY": "${HARDN_API_KEY}" }
    }
  }
}
```

**Cursor**: `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every project. In a file you commit,
take the key from your environment rather than writing it in:

```json
{
  "mcpServers": {
    "hardn": {
      "command": "hardn",
      "args": ["mcp"],
      "env": { "HARDN_API_KEY": "${env:HARDN_API_KEY}" }
    }
  }
}
```

**VS Code (Copilot agent mode)**: `.vscode/mcp.json`. VS Code asks for the key the first time and keeps it in its
own secret storage, so the file holds no key:

```json
{
  "inputs": [
    { "type": "promptString", "id": "hardn-api-key", "description": "Hardn API key (hk_live_…)", "password": true }
  ],
  "servers": {
    "hardn": {
      "type": "stdio",
      "command": "hardn",
      "args": ["mcp"],
      "env": { "HARDN_API_KEY": "${input:hardn-api-key}" }
    }
  }
}
```

`HARDN_API_URL` (or `--api-url` before `mcp`) points it at another Hardn API, for example a local one; leave it unset
for hardn.si. Without a key the server still starts, and each tool says how to set one. When something fails (a wrong
key, an unverified site, no credits left, a network problem), the tool answers with one plain sentence and Hardn's
reference, and the agent can act on it.

Hardn only scans sites whose ownership you have verified. Docs: https://hardn.si/developers
