From 1762ce8be627125e8dd4ad718df0e3b0f6d43165 Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Thu, 29 Oct 2026 04:05:43 +0000 Subject: [PATCH 01/10] docs: Slack agent, full MCP tool list and pentest REST endpoints Adds the @Hacktron Slack agent page, lists every MCP tool with the REST endpoint it mirrors, documents the new pentest, archive upload, credits, finding counts and stop scan endpoints, and syncs openapi.json from the public spec they ship in. Co-Authored-By: Claude Opus 5.5 --- .../archives/request-archive-upload.mdx | 20 + api-reference/credits/get-credit-balance.mdx | 9 + api-reference/findings/count-findings.mdx | 9 + .../pentests/check-pentest-logins.mdx | 15 + api-reference/pentests/create-pentest.mdx | 31 + api-reference/pentests/discard-pentest.mdx | 9 + api-reference/pentests/estimate-pentest.mdx | 13 + api-reference/pentests/start-pentest.mdx | 14 + api-reference/pentests/update-pentest.mdx | 15 + api-reference/scans/stop-scan.mdx | 9 + docs.json | 23 +- mcp/get-started.mdx | 47 +- openapi.json | 948 +++++++++++++++++- platform/communication-apps/slack-agent.mdx | 73 ++ platform/communication-apps/slack.mdx | 8 + white-box-pentest/quickstart.mdx | 31 +- 16 files changed, 1242 insertions(+), 32 deletions(-) create mode 100644 api-reference/archives/request-archive-upload.mdx create mode 100644 api-reference/credits/get-credit-balance.mdx create mode 100644 api-reference/findings/count-findings.mdx create mode 100644 api-reference/pentests/check-pentest-logins.mdx create mode 100644 api-reference/pentests/create-pentest.mdx create mode 100644 api-reference/pentests/discard-pentest.mdx create mode 100644 api-reference/pentests/estimate-pentest.mdx create mode 100644 api-reference/pentests/start-pentest.mdx create mode 100644 api-reference/pentests/update-pentest.mdx create mode 100644 api-reference/scans/stop-scan.mdx create mode 100644 platform/communication-apps/slack-agent.mdx diff --git a/api-reference/archives/request-archive-upload.mdx b/api-reference/archives/request-archive-upload.mdx new file mode 100644 index 0000000..11752e9 --- /dev/null +++ b/api-reference/archives/request-archive-upload.mdx @@ -0,0 +1,20 @@ +--- +title: "Request archive upload" +openapi: "POST /archives/uploads" +description: "Get a URL to upload a code archive to, for code that isn't in a connected repository." +--- + +Returns a signed URL to upload a `.zip`, `.7z` or `.tar.gz` archive of source code to. + +**Scope required**: `write`. **Role**: member or above. + +1. Call this endpoint with the archive's `file_name`. +2. `PUT` the file to `upload_url`, with the `Content-Type` header set to the returned `content_type`. +3. Pentest it by adding `{ "source": "upload", "archive_id": "" }` to `repos` in [Create pentest draft](/api-reference/pentests/create-pentest). + +To pentest only part of the archive, set `include_paths` or `exclude_paths` on that repo instead of uploading a smaller archive. + + + Some self-hosted installs don't support uploads through the API and return + `400`. Upload the archive in the Hacktron web app instead. + diff --git a/api-reference/credits/get-credit-balance.mdx b/api-reference/credits/get-credit-balance.mdx new file mode 100644 index 0000000..62fd70a --- /dev/null +++ b/api-reference/credits/get-credit-balance.mdx @@ -0,0 +1,9 @@ +--- +title: "Get credit balance" +openapi: "GET /credits" +description: "Read the organization's pentest credit balance." +--- + +Returns the pentest credits available to the organization, with totals purchased, used, refunded and granted. `is_unlimited` is true for organizations without a credit limit. + +**Scope required**: `read` diff --git a/api-reference/findings/count-findings.mdx b/api-reference/findings/count-findings.mdx new file mode 100644 index 0000000..438be52 --- /dev/null +++ b/api-reference/findings/count-findings.mdx @@ -0,0 +1,9 @@ +--- +title: "Count findings" +openapi: "GET /findings/counts" +description: "Count findings by severity, state and repository." +--- + +Counts the findings that match the same filters as [List findings](/api-reference/findings/list-findings), without paging through them. The response has the total, counts by severity and by state, and counts per repository with the most findings first. + +**Scope required**: `read` diff --git a/api-reference/pentests/check-pentest-logins.mdx b/api-reference/pentests/check-pentest-logins.mdx new file mode 100644 index 0000000..bd2d7c7 --- /dev/null +++ b/api-reference/pentests/check-pentest-logins.mdx @@ -0,0 +1,15 @@ +--- +title: "Check pentest logins" +openapi: "POST /pentests/{id}/login-checks" +description: "Check that Hacktron can sign in to each login target before the pentest starts." +--- + +Has Hacktron sign in to each of the draft's `login_targets` with its login instructions. Every login must pass before the pentest can start. + +**Scope required**: `write`. **Role**: member or above. + +- A passed check lasts one hour. Start the pentest within the hour, or check again. +- Logins that are already being checked, or that passed within the last hour, aren't checked again unless their site or login instructions changed. +- Set `wait_seconds` (up to 50) to wait for the checks to finish before the response returns. Without it, the response returns while the checks are `pending`. Call again with `wait_seconds` to wait for them. Each call checks failed logins again, so fix their instructions first. + +Each entry in `login_targets` reports its `login_check`: `pending` or `running` while Hacktron signs in, `succeeded` when it signed in, and `failed` when it couldn't. A failed check has a `failure_reason`, which never includes the login details. Fix the instructions with [Update pentest draft](/api-reference/pentests/update-pentest) and check again. diff --git a/api-reference/pentests/create-pentest.mdx b/api-reference/pentests/create-pentest.mdx new file mode 100644 index 0000000..84ebab3 --- /dev/null +++ b/api-reference/pentests/create-pentest.mdx @@ -0,0 +1,31 @@ +--- +title: "Create pentest draft" +openapi: "POST /pentests" +description: "Create a pentest that hasn't started, from repositories or uploaded archives, optionally with live sites." +--- + +Creates a draft pentest. A draft holds the code to review and, optionally, deployed sites to test live. Nothing runs and no credits are spent until you [start it](/api-reference/pentests/start-pentest). + +**Scope required**: `write`. **Role**: member or above. + +## Lifecycle + +1. Create the draft with this endpoint, and change it with [Update pentest draft](/api-reference/pentests/update-pentest). +2. If the draft has `login_targets`, run [Check pentest logins](/api-reference/pentests/check-pentest-logins) until each login passes. +3. Price it with [Estimate pentest](/api-reference/pentests/estimate-pentest). +4. Start it with [Start pentest](/api-reference/pentests/start-pentest), or delete it with [Discard pentest draft](/api-reference/pentests/discard-pentest). + +Keep the `scan_id` from the response. It identifies the pentest from draft to finish, so you can follow it afterwards with [Get scan](/api-reference/scans/get-scan). + +## What to test + +- `repos`: connected repositories, public repositories, or archives uploaded with [Request archive upload](/api-reference/archives/request-archive-upload). Set a repository's `include_paths` and `exclude_paths` to review only part of the code. +- `target_urls`: deployed sites to test without signing in. +- `login_targets`: deployed sites to test signed in. Each has a `site` and `login_instructions`: the login page, username, password and any extra steps. Login instructions are stored encrypted and never returned. + +A pentest tests its live sites either all signed in or all without a login, so one draft can't have both `target_urls` and `login_targets`. Create a separate pentest for the other kind. + + + Signed-in testing is enabled per organization. If it isn't enabled for yours, + a request with `login_targets` returns `400`. + diff --git a/api-reference/pentests/discard-pentest.mdx b/api-reference/pentests/discard-pentest.mdx new file mode 100644 index 0000000..7d8c54b --- /dev/null +++ b/api-reference/pentests/discard-pentest.mdx @@ -0,0 +1,9 @@ +--- +title: "Discard pentest draft" +openapi: "DELETE /pentests/{id}" +description: "Delete a pentest that hasn't started." +--- + +Deletes a draft pentest and its saved login details. A pentest that has started can't be discarded; use [Stop scan](/api-reference/scans/stop-scan) instead. + +**Scope required**: `write`. **Role**: member or above. diff --git a/api-reference/pentests/estimate-pentest.mdx b/api-reference/pentests/estimate-pentest.mdx new file mode 100644 index 0000000..a6734de --- /dev/null +++ b/api-reference/pentests/estimate-pentest.mdx @@ -0,0 +1,13 @@ +--- +title: "Estimate pentest" +openapi: "POST /pentests/{id}/estimate" +description: "Price a draft pentest before starting it." +--- + +Starts a cost estimation for the draft and returns its `cost_estimation_id`. Poll [Get cost estimation](/api-reference/cost-estimations/get-cost-estimation) until its `status` is `completed` or `partial`. + +**Scope required**: `write`. **Role**: member or above. + +The total to confirm when you [start the pentest](/api-reference/pentests/start-pentest) is the estimate's `total_credits`, plus `triage_fee_credits` when `triage_required` is true or you opt in to triage. + +Estimate again whenever you change the draft. diff --git a/api-reference/pentests/start-pentest.mdx b/api-reference/pentests/start-pentest.mdx new file mode 100644 index 0000000..8e36e80 --- /dev/null +++ b/api-reference/pentests/start-pentest.mdx @@ -0,0 +1,14 @@ +--- +title: "Start pentest" +openapi: "POST /pentests/{id}/start" +description: "Start an estimated pentest and spend the confirmed credits." +--- + +Starts a draft pentest that has a completed estimate. Credits are deducted when it starts. + +**Scope required**: `write`. **Role**: organization admin. + +- `expected_credits` is the total you confirmed: the estimate's `total_credits`, plus `triage_fee_credits` when triage is required or you set `triage_enabled`. If the pentest now costs a different amount, the request fails with `409` and nothing is spent. +- Every login target needs a login check that passed within the last hour. See [Check pentest logins](/api-reference/pentests/check-pentest-logins). + +After it starts, follow the pentest with [Get scan status](/api-reference/scans/get-scan-status) using the same `scan_id`, read its results with [List scan findings](/api-reference/scans/list-scan-findings), or stop it with [Stop scan](/api-reference/scans/stop-scan). diff --git a/api-reference/pentests/update-pentest.mdx b/api-reference/pentests/update-pentest.mdx new file mode 100644 index 0000000..9f57b15 --- /dev/null +++ b/api-reference/pentests/update-pentest.mdx @@ -0,0 +1,15 @@ +--- +title: "Update pentest draft" +openapi: "PATCH /pentests/{id}" +description: "Change a pentest that hasn't started." +--- + +Changes a draft pentest. Fields you send replace the saved values, and fields you leave out are kept. A pentest that has started can't be changed. + +**Scope required**: `write`. **Role**: member or above. + +- `login_targets` replaces the whole list, and `[]` removes every login target. Resending a site with the same login instructions keeps its passed login check. +- To fix a login that failed its check, send the site again with new `login_instructions`, then run [Check pentest logins](/api-reference/pentests/check-pentest-logins). +- To switch a draft from signed-in testing to testing without a login (or back), clear the other list in the same request, for example `"login_targets": []` with your `target_urls`. + +Estimate the pentest again after changing it. [Start pentest](/api-reference/pentests/start-pentest) rejects a total that doesn't match the current estimate. diff --git a/api-reference/scans/stop-scan.mdx b/api-reference/scans/stop-scan.mdx new file mode 100644 index 0000000..9708def --- /dev/null +++ b/api-reference/scans/stop-scan.mdx @@ -0,0 +1,9 @@ +--- +title: "Stop scan" +openapi: "POST /scans/{id}/stop" +description: "Stop a running scan or pentest." +--- + +Stops a scan that is running. Members can stop scans they started, and organization admins can stop any scan. + +**Scope required**: `write`. **Role**: member or above. diff --git a/docs.json b/docs.json index 9bc6683..3a74ccc 100644 --- a/docs.json +++ b/docs.json @@ -55,7 +55,8 @@ "group": "Communication Apps", "pages": [ "platform/communication-apps", - "platform/communication-apps/slack" + "platform/communication-apps/slack", + "platform/communication-apps/slack-agent" ] } ] @@ -188,13 +189,31 @@ "api-reference/scans/get-scan", "api-reference/scans/get-scan-status", "api-reference/scans/list-scan-findings", - "api-reference/scans/export-scan-findings" + "api-reference/scans/export-scan-findings", + "api-reference/scans/stop-scan" ] }, + { + "group": "Pentests", + "pages": [ + "api-reference/pentests/create-pentest", + "api-reference/pentests/update-pentest", + "api-reference/pentests/check-pentest-logins", + "api-reference/pentests/estimate-pentest", + "api-reference/pentests/start-pentest", + "api-reference/pentests/discard-pentest", + "api-reference/archives/request-archive-upload" + ] + }, + { + "group": "Credits", + "pages": ["api-reference/credits/get-credit-balance"] + }, { "group": "Findings", "pages": [ "api-reference/findings/list-findings", + "api-reference/findings/count-findings", "api-reference/findings/get-finding", "api-reference/findings/update-finding", "api-reference/findings/add-finding-comment", diff --git a/mcp/get-started.mdx b/mcp/get-started.mdx index 54304e8..402bb39 100644 --- a/mcp/get-started.mdx +++ b/mcp/get-started.mdx @@ -172,15 +172,42 @@ description: "Choose your platform and copy the config. No API key required." ## Tools -| Name | Description | -| --------------------- | ----------------------------------------------- | -| `list_scans` | List scans with severity breakdown | -| `get_scan` | Get details about a scan | -| `export_findings` | Export a scan's findings as JSON, CSV, or SARIF | -| `list_findings` | List findings across the organisation | -| `get_finding` | Get details about a finding | -| `update_finding` | Update a finding's state or severity | -| `add_finding_comment` | Post a comment on a finding | +Each tool calls the same endpoint as the [REST API](/api-reference/introduction), with the same permissions. Viewers can read, members can change findings and set up pentests, and only organization admins can start a pentest. + +### Scans and findings + +| Name | Description | REST endpoint | +| --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------ | +| `list_scans` | List scans with severity breakdown | [`GET /scans`](/api-reference/scans/list-scans) | +| `get_scan` | Get details about a scan | [`GET /scans/{id}`](/api-reference/scans/get-scan) | +| `export_findings` | Export a scan's findings as JSON, CSV, or SARIF | [`GET /scans/{id}/findings/export`](/api-reference/scans/export-scan-findings) | +| `stop_scan` | Stop a running scan or pentest | [`POST /scans/{id}/stop`](/api-reference/scans/stop-scan) | +| `list_findings` | List findings across the organisation | [`GET /findings`](/api-reference/findings/list-findings) | +| `count_findings` | Count findings by severity, state and repository | [`GET /findings/counts`](/api-reference/findings/count-findings) | +| `get_finding` | Get details about a finding | [`GET /findings/{id}`](/api-reference/findings/get-finding) | +| `update_finding` | Update a finding's state or severity | [`PATCH /findings/{id}`](/api-reference/findings/update-finding) | +| `add_finding_comment` | Post a comment on a finding | [`POST /findings/{id}/comments`](/api-reference/findings/add-finding-comment) | + +### Pentests + +| Name | Description | REST endpoint | +| ------------------------ | ---------------------------------------------------- | ----------------------------------------------------------------------------------- | +| `list_repositories` | List connected repositories | [`GET /repos`](/api-reference/repos/list-repos) | +| `request_archive_upload` | Get a URL to upload a code archive | [`POST /archives/uploads`](/api-reference/archives/request-archive-upload) | +| `create_pentest_draft` | Set up a pentest of code, optionally with live sites | [`POST /pentests`](/api-reference/pentests/create-pentest) | +| `update_pentest_draft` | Change a pentest that hasn't started | [`PATCH /pentests/{id}`](/api-reference/pentests/update-pentest) | +| `verify_pentest_login` | Check that Hacktron can sign in to each site | [`POST /pentests/{id}/login-checks`](/api-reference/pentests/check-pentest-logins) | +| `estimate_pentest` | Price a pentest | [`POST /pentests/{id}/estimate`](/api-reference/pentests/estimate-pentest) | +| `get_cost_estimation` | Get a pentest cost estimate | [`GET /cost-estimations/{id}`](/api-reference/cost-estimations/get-cost-estimation) | +| `get_credit_balance` | Get the organisation's pentest credit balance | [`GET /credits`](/api-reference/credits/get-credit-balance) | +| `start_pentest` | Start a pentest and spend its credits (admins only) | [`POST /pentests/{id}/start`](/api-reference/pentests/start-pentest) | +| `discard_pentest_draft` | Delete a pentest that hasn't started | [`DELETE /pentests/{id}`](/api-reference/pentests/discard-pentest) | + + + To test a site signed in, your assistant sends the login details to Hacktron, + where they're stored encrypted. Anything you type in the conversation also + goes to your assistant's model provider, so use a test account. + ## Related @@ -193,6 +220,6 @@ description: "Choose your platform and copy the config. No API key required." Create and scope the API key used for API-key auth. - The same scans and findings, over the REST API. + Every tool, over the REST API. diff --git a/openapi.json b/openapi.json index 9fb975b..46c67d6 100644 --- a/openapi.json +++ b/openapi.json @@ -137,6 +137,37 @@ ] } }, + "/scans/{id}/stop": { + "post": { + "operationId": "RestScanController_stop", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPentestResponseDto" + } + } + } + } + }, + "summary": "Stop a running scan. Members can stop scans they started; admins can stop any.", + "tags": [ + "REST API - Scans" + ] + } + }, "/scans/{id}/status": { "get": { "operationId": "RestScanController_getStatus", @@ -541,6 +572,108 @@ ] } }, + "/findings/counts": { + "get": { + "operationId": "RestFindingController_counts", + "parameters": [ + { + "name": "severity", + "required": false, + "in": "query", + "schema": { + "type": "string", + "enum": [ + "critical", + "high", + "medium", + "low", + "info" + ] + } + }, + { + "name": "state", + "required": false, + "in": "query", + "schema": { + "type": "string", + "enum": [ + "open", + "true_positive", + "false_positive", + "accepted_risk", + "resolved", + "closed" + ] + } + }, + { + "name": "scan_id", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "scan_type", + "required": false, + "in": "query", + "description": "Filter by scan type across all scans associated with the finding.", + "schema": { + "type": "string", + "enum": [ + "pr", + "full" + ] + } + }, + { + "name": "repo_url", + "required": false, + "in": "query", + "description": "Filter by repository URL. Case-insensitive; a trailing slash or .git suffix is ignored.", + "schema": { + "type": "string" + } + }, + { + "name": "found_since", + "required": false, + "in": "query", + "description": "Only include findings discovered on/after this ISO 8601 timestamp.", + "schema": { + "type": "string" + } + }, + { + "name": "found_until", + "required": false, + "in": "query", + "description": "Only include findings discovered before this ISO 8601 timestamp.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestFindingCountsResponseDto" + } + } + } + } + }, + "summary": "Count findings matching the list filters, by severity, state and repository", + "tags": [ + "REST API - Findings" + ] + } + }, "/findings/export": { "get": { "operationId": "RestFindingController_exportFindings", @@ -1101,6 +1234,276 @@ "REST API - HFW" ] } + }, + "/pentests": { + "post": { + "operationId": "RestPentestController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestCreatePentestDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPentestResponseDto" + } + } + } + } + }, + "summary": "Create a draft pentest of repositories or uploaded archives, optionally with live sites", + "tags": [ + "REST API - Pentests" + ] + } + }, + "/pentests/{id}": { + "patch": { + "operationId": "RestPentestController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestUpdatePentestDto" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPentestResponseDto" + } + } + } + } + }, + "summary": "Change a pentest that has not started", + "tags": [ + "REST API - Pentests" + ] + }, + "delete": { + "operationId": "RestPentestController_discard", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestDiscardPentestResponseDto" + } + } + } + } + }, + "summary": "Discard a pentest that has not started", + "tags": [ + "REST API - Pentests" + ] + } + }, + "/pentests/{id}/login-checks": { + "post": { + "operationId": "RestPentestController_checkLogins", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + }, + { + "name": "wait_seconds", + "required": false, + "in": "query", + "description": "Wait up to this many seconds for the work to finish before answering", + "schema": { + "minimum": 0, + "maximum": 50, + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPentestResponseDto" + } + } + } + } + }, + "summary": "Check that Hacktron can sign in to each login target, which it must before the pentest starts", + "tags": [ + "REST API - Pentests" + ] + } + }, + "/pentests/{id}/estimate": { + "post": { + "operationId": "RestPentestController_estimate", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPentestResponseDto" + } + } + } + } + }, + "summary": "Price a draft pentest; read the estimate at GET /rest/cost-estimations/{cost_estimation_id}", + "tags": [ + "REST API - Pentests" + ] + } + }, + "/pentests/{id}/start": { + "post": { + "operationId": "RestPentestController_start", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestStartPentestDto" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPentestResponseDto" + } + } + } + } + }, + "summary": "Start an estimated pentest, spending the confirmed credits (organization admins only)", + "tags": [ + "REST API - Pentests" + ] + } + }, + "/archives/uploads": { + "post": { + "operationId": "RestArchiveController_requestUpload", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestUploadDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestUploadResponseDto" + } + } + } + } + }, + "summary": "Get a URL to upload a code archive (.zip, .7z or .tar.gz) to, then pentest it as a repo with source upload", + "tags": [ + "REST API - Archives" + ] + } + }, + "/credits": { + "get": { + "operationId": "RestCreditController_balance", + "parameters": [], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PentestCreditBalanceResponseDto" + } + } + } + } + }, + "summary": "The organization's pentest credit balance", + "tags": [ + "REST API - Credits" + ] + } } }, "info": { @@ -1201,21 +1604,203 @@ "id": { "type": "string" }, - "task_id": { - "type": "string" + "task_id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "id", + "task_id", + "status", + "message" + ] + }, + "RepoDataDto": { + "type": "object", + "properties": { + "include_paths": { + "description": "Whitebox scope: gitignore-style globs (relative to the repo root) that narrow the scan to matching paths. Empty means the whole repository.", + "example": [ + "services/gateway/**" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "exclude_paths": { + "description": "Whitebox scope: gitignore-style globs removed from the scan after include_paths is applied; use `*.md` to drop a file type.", + "example": [ + "**/tests/**", + "vendor/", + "*.md" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string", + "enum": [ + "connected", + "public", + "upload" + ] + }, + "provider": { + "type": "string", + "enum": [ + "github", + "gitlab", + "bitbucket", + "azure" + ], + "readOnly": true, + "description": "Resolved by the server at scan start; ignored on input" + }, + "url": { + "type": "string", + "description": "Repository URL", + "example": "https://github.com/owner/repo" + }, + "branch": { + "type": "string", + "description": "Branch name, tag name, or commit SHA" + }, + "ref_type": { + "type": "string", + "enum": [ + "branch", + "tag", + "sha" + ], + "description": "How `branch` was selected (UI hint for draft re-use)" + }, + "archive_id": { + "type": "string", + "description": "Scan archive ID" + }, + "github_installation_id": { + "type": "number", + "description": "GitHub App installation ID (connected repos only)" + } + } + }, + "RestPentestLoginTargetDto": { + "type": "object", + "properties": { + "site": { + "type": "string" + }, + "login_check": { + "type": "string", + "enum": [ + "unverified", + "pending", + "running", + "succeeded", + "failed", + "cancelled", + "expired" + ], + "description": "Whether Hacktron could sign in; a pass lasts an hour" + }, + "failure_reason": { + "type": "string", + "nullable": true, + "description": "Why the last check failed, without credentials" + } + }, + "required": [ + "site", + "login_check", + "failure_reason" + ] + }, + "RestPentestResponseDto": { + "type": "object", + "properties": { + "scan_id": { + "type": "string", + "format": "uuid", + "description": "Stable pentest id; task_id changes when it starts" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "running", + "completed", + "failed", + "stopped", + "cancelled", + "skipped", + "pending_verification", + "pending_triage", + "draft", + "estimating", + "estimated" + ] + }, + "repos": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RepoDataDto" + } + }, + "target_urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "login_targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RestPentestLoginTargetDto" + } + }, + "custom_context": { + "type": "string", + "nullable": true }, - "status": { - "type": "string" + "application_id": { + "type": "string", + "nullable": true, + "format": "uuid" }, - "message": { - "type": "string" + "cost_estimation_id": { + "type": "string", + "nullable": true, + "format": "uuid" + }, + "triage_enabled": { + "type": "boolean", + "nullable": true + }, + "credits_used": { + "type": "number", + "nullable": true } }, "required": [ - "id", - "task_id", + "scan_id", "status", - "message" + "repos", + "target_urls", + "login_targets", + "custom_context", + "application_id", + "cost_estimation_id", + "triage_enabled", + "credits_used" ] }, "RestScanFindingsSummaryDto": { @@ -1503,6 +2088,97 @@ "limit" ] }, + "RestSeverityCountsDto": { + "type": "object", + "properties": { + "critical": { + "type": "number" + }, + "high": { + "type": "number" + }, + "medium": { + "type": "number" + }, + "low": { + "type": "number" + }, + "info": { + "type": "number" + } + } + }, + "RestStateCountsDto": { + "type": "object", + "properties": { + "open": { + "type": "number" + }, + "true_positive": { + "type": "number" + }, + "false_positive": { + "type": "number" + }, + "accepted_risk": { + "type": "number" + }, + "resolved": { + "type": "number" + }, + "closed": { + "type": "number" + } + } + }, + "RestRepositoryFindingCountsDto": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "nullable": true, + "description": "Repository URL, or null for findings with no repository" + }, + "total": { + "type": "number" + }, + "by_severity": { + "$ref": "#/components/schemas/RestSeverityCountsDto" + } + }, + "required": [ + "repository", + "total", + "by_severity" + ] + }, + "RestFindingCountsResponseDto": { + "type": "object", + "properties": { + "total": { + "type": "number" + }, + "by_severity": { + "$ref": "#/components/schemas/RestSeverityCountsDto" + }, + "by_state": { + "$ref": "#/components/schemas/RestStateCountsDto" + }, + "by_repository": { + "description": "Most findings first", + "type": "array", + "items": { + "$ref": "#/components/schemas/RestRepositoryFindingCountsDto" + } + } + }, + "required": [ + "total", + "by_severity", + "by_state", + "by_repository" + ] + }, "RestUpdateFindingDto": { "type": "object", "properties": { @@ -2495,6 +3171,260 @@ "required": [ "results" ] + }, + "RestLoginTargetDto": { + "type": "object", + "properties": { + "site": { + "type": "string", + "description": "A deployed copy of the app to test signed in, e.g. https://staging.acme.dev", + "maxLength": 2048 + }, + "login_instructions": { + "type": "string", + "description": "How to sign in: the login page, username, password and any steps. Stored encrypted and never returned.", + "maxLength": 2000 + } + }, + "required": [ + "site", + "login_instructions" + ] + }, + "RestCreatePentestDto": { + "type": "object", + "properties": { + "repos": { + "type": "array", + "description": "Multiple repositories (takes precedence over repo_url/branch)", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ConnectedRepoInputDto" + }, + { + "$ref": "#/components/schemas/PublicRepoInputDto" + }, + { + "$ref": "#/components/schemas/UploadRepoInputDto" + } + ], + "discriminator": { + "propertyName": "source", + "mapping": { + "connected": "#/components/schemas/ConnectedRepoInputDto", + "public": "#/components/schemas/PublicRepoInputDto", + "upload": "#/components/schemas/UploadRepoInputDto" + } + } + } + }, + "target_urls": { + "description": "Target URLs (pentest only)", + "type": "array", + "items": { + "type": "string" + } + }, + "custom_context": { + "type": "string", + "description": "Custom context (pentest only)" + }, + "application_id": { + "type": "string", + "description": "iva application this scan targets (group of repos)" + }, + "login_targets": { + "description": "Deployed sites to test signed in, each with how to log in, replacing any saved ones ([] removes them). A pentest tests live sites either all signed in or all without a login (target_urls). Check each login with POST /rest/pentests/{id}/login-checks before starting.", + "maxItems": 10, + "type": "array", + "items": { + "$ref": "#/components/schemas/RestLoginTargetDto" + } + } + } + }, + "RestUpdatePentestDto": { + "type": "object", + "properties": { + "repos": { + "type": "array", + "description": "Multiple repositories (takes precedence over repo_url/branch)", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ConnectedRepoInputDto" + }, + { + "$ref": "#/components/schemas/PublicRepoInputDto" + }, + { + "$ref": "#/components/schemas/UploadRepoInputDto" + } + ], + "discriminator": { + "propertyName": "source", + "mapping": { + "connected": "#/components/schemas/ConnectedRepoInputDto", + "public": "#/components/schemas/PublicRepoInputDto", + "upload": "#/components/schemas/UploadRepoInputDto" + } + } + } + }, + "target_urls": { + "description": "Target URLs (pentest only)", + "type": "array", + "items": { + "type": "string" + } + }, + "custom_context": { + "type": "string", + "description": "Custom context (pentest only)" + }, + "application_id": { + "type": "string", + "description": "iva application this scan targets (group of repos)" + }, + "login_targets": { + "description": "Deployed sites to test signed in, each with how to log in, replacing any saved ones ([] removes them). A pentest tests live sites either all signed in or all without a login (target_urls). Check each login with POST /rest/pentests/{id}/login-checks before starting.", + "maxItems": 10, + "type": "array", + "items": { + "$ref": "#/components/schemas/RestLoginTargetDto" + } + } + } + }, + "RestStartPentestDto": { + "type": "object", + "properties": { + "expected_credits": { + "type": "number", + "description": "The total the user confirmed: the estimate's total_credits, plus triage_fee_credits when triage is required or opted in", + "minimum": 1 + }, + "triage_enabled": { + "type": "boolean", + "description": "Opt in to human triage of findings when it is not already required" + } + }, + "required": [ + "expected_credits" + ] + }, + "RestDiscardPentestResponseDto": { + "type": "object", + "properties": { + "scan_id": { + "type": "string", + "format": "uuid" + }, + "discarded": { + "type": "boolean" + } + }, + "required": [ + "scan_id", + "discarded" + ] + }, + "RequestUploadDto": { + "type": "object", + "properties": { + "file_name": { + "type": "string", + "description": "Original filename (e.g. \"my-project.zip\")", + "example": "my-project.zip" + }, + "name": { + "type": "string", + "description": "Display name (defaults to filename)" + } + }, + "required": [ + "file_name" + ] + }, + "RequestUploadResponseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Archive ID" + }, + "upload_url": { + "type": "string", + "description": "Signed GCS upload URL (PUT)" + }, + "content_type": { + "type": "string", + "description": "Content-Type to use in the PUT request", + "example": "application/zip" + } + }, + "required": [ + "id", + "upload_url", + "content_type" + ] + }, + "PentestCreditBalanceResponseDto": { + "type": "object", + "properties": { + "organization_id": { + "type": "string", + "description": "Organization UUID", + "example": "f336d0bc-b841-465b-8045-024475c079dd" + }, + "balance": { + "type": "number", + "description": "Current pentest credit balance available for use", + "example": 12000 + }, + "total_purchased": { + "type": "number", + "description": "Total pentest credits purchased across all time", + "example": 16000 + }, + "total_used": { + "type": "number", + "description": "Total pentest credits deducted for completed/running pentests", + "example": 8000 + }, + "total_refunded": { + "type": "number", + "description": "Total pentest credits refunded from cancelled pentests", + "example": 4000 + }, + "total_granted": { + "type": "number", + "description": "Total pentest credits granted at no charge", + "example": 2000 + }, + "used_this_period": { + "type": "number", + "nullable": true, + "description": "Credits deducted since the start of the current billing period, null when the organization has no billing period", + "example": 1240 + }, + "is_unlimited": { + "type": "boolean", + "description": "Whether this organization has internal unlimited pentest entitlement", + "example": false + } + }, + "required": [ + "organization_id", + "balance", + "total_purchased", + "total_used", + "total_refunded", + "total_granted", + "used_this_period", + "is_unlimited" + ] } } }, diff --git a/platform/communication-apps/slack-agent.mdx b/platform/communication-apps/slack-agent.mdx new file mode 100644 index 0000000..f209043 --- /dev/null +++ b/platform/communication-apps/slack-agent.mdx @@ -0,0 +1,73 @@ +--- +title: "Ask Hacktron in Slack" +description: "Mention @Hacktron to ask about findings and set up pentests from Slack." +--- + +Mention **@Hacktron** in a channel, or send it a direct message, to ask about your scans and findings or to set up a pentest. It replies in a thread. Keep talking in that thread without mentioning it again. + +## Before you start + +- An organization admin has [connected Slack](/platform/communication-apps/slack) to Hacktron. +- You've [linked your Slack account](/code-review/integrations/slack#link-your-slack-identity) to your Hacktron account. Hacktron acts as you, with your role: viewers can ask questions, members can set up pentests, and organization admins can start them. +- To mention Hacktron in a channel, add the Hacktron app to that channel first. + +## Ask about findings + +Ask in your own words, for example: + +- "How many critical findings are open in acme/api?" +- "Show me the high severity findings from the last pentest" +- "Which repositories have the most open findings?" + +Hacktron answers with counts and posts findings as cards that link to them in Hacktron. + +## Set up a pentest + + + + Name a connected repository, or attach a `.zip`, `.7z` or `.tar.gz` archive + of the code (up to 2 GB) to your message. + + + To test only part of the code, say so in the thread, for example "only the + services/payments folder". There's no need to upload a smaller archive. + + + Give Hacktron the URL of a deployed copy of the app to test it live as well + as the code. A staging copy is the safest choice. Without one, the pentest + reviews the code only. + + + Hacktron estimates the pentest and asks you to confirm. + + + Hacktron posts an approval card. An organization admin clicks **Start + pentest**. Credits are spent when the pentest starts. + + + When the pentest finishes, Hacktron posts the results in the same thread. + + + +### Test a site signed in + +If the live site needs a login, Hacktron posts an **Add login details** button. It opens a private form that only you can see. The details go straight to Hacktron, are stored encrypted, and never appear in the thread or reach the assistant. + +Hacktron then checks that it can sign in and posts the result in the thread. If the check fails, click **Update login details** to fix them. + +A pentest tests its live sites either all signed in or all without a login. To test both kinds, set up a separate pentest for each. + + + Signed-in testing is enabled per organization. If it isn't available for + yours, Hacktron says so and can still test the site without signing in. + + +## Follow what it's doing + +While it works, Hacktron shows its plan and each step it takes in a progress card at the top of its reply. + +## Limits + +- Hacktron reads attached archives to understand the code, but never runs your code. To test the running app, give it a deployed site. +- Each thread has a usage limit. When a thread reaches it, Hacktron stops and asks you to mention it in a new thread. +- If Hacktron can't read files you attach, ask an organization admin to reconnect Slack from **Integrations** so the app gets permission to read files. diff --git a/platform/communication-apps/slack.mdx b/platform/communication-apps/slack.mdx index a0e1012..92af57f 100644 --- a/platform/communication-apps/slack.mdx +++ b/platform/communication-apps/slack.mdx @@ -60,3 +60,11 @@ Open the Slack card in **Integrations** and choose **Disconnect**. Hacktron will - If Slack does not connect, retry from **Integrations** so Hacktron can create a fresh OAuth state. - If a channel is missing, confirm that the Slack workspace account used for OAuth can access that channel. - If notifications are not appearing, confirm that the Slack integration is connected and a channel is selected. + + + Mention @Hacktron to ask about findings and set up pentests. + diff --git a/white-box-pentest/quickstart.mdx b/white-box-pentest/quickstart.mdx index 49094ae..f10f1f4 100644 --- a/white-box-pentest/quickstart.mdx +++ b/white-box-pentest/quickstart.mdx @@ -197,28 +197,37 @@ For a public target: | Exclusions | Systems, data, tenants, destructive actions, or rate limits that are out of scope. | | Context documents | Architecture notes, threat models, API specs, or prior pentest reports. | -## API option +## Other ways to start a pentest -You can also start Whitebox Scans from the REST API. The API follows the -same structure as the dashboard flow: create a cost estimation first, wait for a -completed or partial estimate with credits, then start the scan with the same -repositories, branches, `include_paths`, and `exclude_paths`. Create a new -estimate if you change the scope. +You can also set up and start a pentest from Slack, from an AI assistant +connected to the Hacktron MCP server, or from the REST API. Each follows the +same steps as the dashboard: set the scope, estimate the credits, then start. +Only organization admins can start a pentest. - Estimate the credit cost for one or more repositories. + Mention @Hacktron with a repository or an attached archive. + + + Set up pentests from Claude, Cursor, and other AI assistants. + + + Set up a pentest of code and live sites over the REST API. - Start a Whitebox Scan after the cost estimate is ready. + Start a code-only scan from a completed cost estimation. From bfde44f91893b92642c928b90efaf91c6bc76e21 Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Thu, 29 Oct 2026 04:40:51 +0000 Subject: [PATCH 02/10] docs: match the pentest API review fixes Sync openapi.json from the fixed public spec (repos schemas now defined), and document that repos is required, changing repos clears the estimate, a changed login reads unverified, start answers 402 without credits, stop returns the scan status, and finding counts list every severity and state. Co-Authored-By: Claude Opus 5.5 --- api-reference/findings/count-findings.mdx | 2 +- api-reference/pentests/create-pentest.mdx | 2 +- api-reference/pentests/start-pentest.mdx | 2 +- api-reference/pentests/update-pentest.mdx | 3 +- api-reference/scans/stop-scan.mdx | 2 +- openapi.json | 694 ++++++++++++++-------- 6 files changed, 448 insertions(+), 257 deletions(-) diff --git a/api-reference/findings/count-findings.mdx b/api-reference/findings/count-findings.mdx index 438be52..fc97010 100644 --- a/api-reference/findings/count-findings.mdx +++ b/api-reference/findings/count-findings.mdx @@ -4,6 +4,6 @@ openapi: "GET /findings/counts" description: "Count findings by severity, state and repository." --- -Counts the findings that match the same filters as [List findings](/api-reference/findings/list-findings), without paging through them. The response has the total, counts by severity and by state, and counts per repository with the most findings first. +Counts the findings that match the same filters as [List findings](/api-reference/findings/list-findings), without paging through them. The response has the total, counts for every severity and state (zero when there are none), and counts per repository with the most findings first. **Scope required**: `read` diff --git a/api-reference/pentests/create-pentest.mdx b/api-reference/pentests/create-pentest.mdx index 84ebab3..434bb58 100644 --- a/api-reference/pentests/create-pentest.mdx +++ b/api-reference/pentests/create-pentest.mdx @@ -19,7 +19,7 @@ Keep the `scan_id` from the response. It identifies the pentest from draft to fi ## What to test -- `repos`: connected repositories, public repositories, or archives uploaded with [Request archive upload](/api-reference/archives/request-archive-upload). Set a repository's `include_paths` and `exclude_paths` to review only part of the code. +- `repos` (at least one): connected repositories, public repositories, or archives uploaded with [Request archive upload](/api-reference/archives/request-archive-upload). Set a repository's `include_paths` and `exclude_paths` to review only part of the code. - `target_urls`: deployed sites to test without signing in. - `login_targets`: deployed sites to test signed in. Each has a `site` and `login_instructions`: the login page, username, password and any extra steps. Login instructions are stored encrypted and never returned. diff --git a/api-reference/pentests/start-pentest.mdx b/api-reference/pentests/start-pentest.mdx index 8e36e80..b687fa5 100644 --- a/api-reference/pentests/start-pentest.mdx +++ b/api-reference/pentests/start-pentest.mdx @@ -8,7 +8,7 @@ Starts a draft pentest that has a completed estimate. Credits are deducted when **Scope required**: `write`. **Role**: organization admin. -- `expected_credits` is the total you confirmed: the estimate's `total_credits`, plus `triage_fee_credits` when triage is required or you set `triage_enabled`. If the pentest now costs a different amount, the request fails with `409` and nothing is spent. +- `expected_credits` is the total you confirmed: the estimate's `total_credits`, plus `triage_fee_credits` when triage is required or you set `triage_enabled`. If the pentest now costs a different amount, the request fails with `409` and nothing is spent. Without enough credits it fails with `402`. - Every login target needs a login check that passed within the last hour. See [Check pentest logins](/api-reference/pentests/check-pentest-logins). After it starts, follow the pentest with [Get scan status](/api-reference/scans/get-scan-status) using the same `scan_id`, read its results with [List scan findings](/api-reference/scans/list-scan-findings), or stop it with [Stop scan](/api-reference/scans/stop-scan). diff --git a/api-reference/pentests/update-pentest.mdx b/api-reference/pentests/update-pentest.mdx index 9f57b15..a0e1fbe 100644 --- a/api-reference/pentests/update-pentest.mdx +++ b/api-reference/pentests/update-pentest.mdx @@ -8,7 +8,8 @@ Changes a draft pentest. Fields you send replace the saved values, and fields yo **Scope required**: `write`. **Role**: member or above. -- `login_targets` replaces the whole list, and `[]` removes every login target. Resending a site with the same login instructions keeps its passed login check. +- `repos` replaces the saved repositories and needs at least one. Changing them clears the estimate. +- `login_targets` replaces the whole list, and `[]` removes every login target. Resending a site with the same login instructions keeps its passed login check. A changed site or login reports `login_check: unverified` until you check it again. - To fix a login that failed its check, send the site again with new `login_instructions`, then run [Check pentest logins](/api-reference/pentests/check-pentest-logins). - To switch a draft from signed-in testing to testing without a login (or back), clear the other list in the same request, for example `"login_targets": []` with your `target_urls`. diff --git a/api-reference/scans/stop-scan.mdx b/api-reference/scans/stop-scan.mdx index 9708def..db86295 100644 --- a/api-reference/scans/stop-scan.mdx +++ b/api-reference/scans/stop-scan.mdx @@ -4,6 +4,6 @@ openapi: "POST /scans/{id}/stop" description: "Stop a running scan or pentest." --- -Stops a scan that is running. Members can stop scans they started, and organization admins can stop any scan. +Stops a scan that is running and returns its status. Members can stop pentests they started and any PR scan, and organization admins can stop any scan. **Scope required**: `write`. **Role**: member or above. diff --git a/openapi.json b/openapi.json index 46c67d6..72bc56b 100644 --- a/openapi.json +++ b/openapi.json @@ -156,13 +156,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RestPentestResponseDto" + "$ref": "#/components/schemas/RestScanStatusResponseDto" } } } } }, - "summary": "Stop a running scan. Members can stop scans they started; admins can stop any.", + "summary": "Stop a running scan. Members can stop pentests they started and any PR scan; admins can stop any scan.", "tags": [ "REST API - Scans" ] @@ -1302,7 +1302,7 @@ } } }, - "summary": "Change a pentest that has not started", + "summary": "Change a pentest that has not started. Changing its repositories clears the estimate.", "tags": [ "REST API - Pentests" ] @@ -1357,7 +1357,8 @@ "schema": { "minimum": 0, "maximum": 50, - "type": "number" + "default": 0, + "type": "integer" } } ], @@ -1371,6 +1372,9 @@ } } } + }, + "400": { + "description": "The pentest has no login targets" } }, "summary": "Check that Hacktron can sign in to each login target, which it must before the pentest starts", @@ -1404,7 +1408,7 @@ } } }, - "summary": "Price a draft pentest; read the estimate at GET /rest/cost-estimations/{cost_estimation_id}", + "summary": "Price a draft pentest; read it at GET /cost-estimations/{cost_estimation_id}", "tags": [ "REST API - Pentests" ] @@ -1443,6 +1447,15 @@ } } } + }, + "400": { + "description": "A login target has no passing login check from the last hour" + }, + "402": { + "description": "Insufficient credits" + }, + "409": { + "description": "expected_credits no longer matches the total; read the estimate again and confirm the new total" } }, "summary": "Start an estimated pentest, spending the confirmed credits (organization admins only)", @@ -1621,116 +1634,14 @@ "message" ] }, - "RepoDataDto": { - "type": "object", - "properties": { - "include_paths": { - "description": "Whitebox scope: gitignore-style globs (relative to the repo root) that narrow the scan to matching paths. Empty means the whole repository.", - "example": [ - "services/gateway/**" - ], - "type": "array", - "items": { - "type": "string" - } - }, - "exclude_paths": { - "description": "Whitebox scope: gitignore-style globs removed from the scan after include_paths is applied; use `*.md` to drop a file type.", - "example": [ - "**/tests/**", - "vendor/", - "*.md" - ], - "type": "array", - "items": { - "type": "string" - } - }, - "source": { - "type": "string", - "enum": [ - "connected", - "public", - "upload" - ] - }, - "provider": { - "type": "string", - "enum": [ - "github", - "gitlab", - "bitbucket", - "azure" - ], - "readOnly": true, - "description": "Resolved by the server at scan start; ignored on input" - }, - "url": { - "type": "string", - "description": "Repository URL", - "example": "https://github.com/owner/repo" - }, - "branch": { - "type": "string", - "description": "Branch name, tag name, or commit SHA" - }, - "ref_type": { - "type": "string", - "enum": [ - "branch", - "tag", - "sha" - ], - "description": "How `branch` was selected (UI hint for draft re-use)" - }, - "archive_id": { - "type": "string", - "description": "Scan archive ID" - }, - "github_installation_id": { - "type": "number", - "description": "GitHub App installation ID (connected repos only)" - } - } - }, - "RestPentestLoginTargetDto": { + "RestScanStatusResponseDto": { "type": "object", "properties": { - "site": { + "id": { "type": "string" }, - "login_check": { - "type": "string", - "enum": [ - "unverified", - "pending", - "running", - "succeeded", - "failed", - "cancelled", - "expired" - ], - "description": "Whether Hacktron could sign in; a pass lasts an hour" - }, - "failure_reason": { - "type": "string", - "nullable": true, - "description": "Why the last check failed, without credentials" - } - }, - "required": [ - "site", - "login_check", - "failure_reason" - ] - }, - "RestPentestResponseDto": { - "type": "object", - "properties": { - "scan_id": { - "type": "string", - "format": "uuid", - "description": "Stable pentest id; task_id changes when it starts" + "task_id": { + "type": "string" }, "status": { "type": "string", @@ -1749,58 +1660,19 @@ "estimated" ] }, - "repos": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RepoDataDto" - } - }, - "target_urls": { - "type": "array", - "items": { - "type": "string" - } - }, - "login_targets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestPentestLoginTargetDto" - } - }, - "custom_context": { - "type": "string", - "nullable": true - }, - "application_id": { - "type": "string", - "nullable": true, - "format": "uuid" - }, - "cost_estimation_id": { - "type": "string", - "nullable": true, - "format": "uuid" - }, - "triage_enabled": { - "type": "boolean", - "nullable": true + "created_at": { + "type": "string" }, - "credits_used": { - "type": "number", - "nullable": true + "updated_at": { + "type": "string" } }, "required": [ - "scan_id", + "id", + "task_id", "status", - "repos", - "target_urls", - "login_targets", - "custom_context", - "application_id", - "cost_estimation_id", - "triage_enabled", - "credits_used" + "created_at", + "updated_at" ] }, "RestScanFindingsSummaryDto": { @@ -1923,47 +1795,6 @@ "limit" ] }, - "RestScanStatusResponseDto": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "task_id": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "pending", - "running", - "completed", - "failed", - "stopped", - "cancelled", - "skipped", - "pending_verification", - "pending_triage", - "draft", - "estimating", - "estimated" - ] - }, - "created_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "required": [ - "id", - "task_id", - "status", - "created_at", - "updated_at" - ] - }, "RestFindingResponseDto": { "type": "object", "properties": { @@ -2106,7 +1937,14 @@ "info": { "type": "number" } - } + }, + "required": [ + "critical", + "high", + "medium", + "low", + "info" + ] }, "RestStateCountsDto": { "type": "object", @@ -2129,7 +1967,15 @@ "closed": { "type": "number" } - } + }, + "required": [ + "open", + "true_positive", + "false_positive", + "accepted_risk", + "resolved", + "closed" + ] }, "RestRepositoryFindingCountsDto": { "type": "object", @@ -3172,53 +3018,185 @@ "results" ] }, - "RestLoginTargetDto": { - "type": "object", - "properties": { - "site": { - "type": "string", - "description": "A deployed copy of the app to test signed in, e.g. https://staging.acme.dev", - "maxLength": 2048 - }, - "login_instructions": { - "type": "string", - "description": "How to sign in: the login page, username, password and any steps. Stored encrypted and never returned.", - "maxLength": 2000 - } - }, - "required": [ - "site", - "login_instructions" - ] - }, - "RestCreatePentestDto": { + "ConnectedRepoInputDto": { "type": "object", "properties": { - "repos": { + "include_paths": { + "description": "Whitebox scope: gitignore-style globs (relative to the repo root) that narrow the scan to matching paths. Empty means the whole repository.", + "example": [ + "services/gateway/**" + ], "type": "array", - "description": "Multiple repositories (takes precedence over repo_url/branch)", "items": { - "oneOf": [ - { - "$ref": "#/components/schemas/ConnectedRepoInputDto" - }, - { - "$ref": "#/components/schemas/PublicRepoInputDto" - }, - { - "$ref": "#/components/schemas/UploadRepoInputDto" - } - ], - "discriminator": { - "propertyName": "source", - "mapping": { - "connected": "#/components/schemas/ConnectedRepoInputDto", - "public": "#/components/schemas/PublicRepoInputDto", - "upload": "#/components/schemas/UploadRepoInputDto" - } - } + "type": "string" + } + }, + "exclude_paths": { + "description": "Whitebox scope: gitignore-style globs removed from the scan after include_paths is applied; use `*.md` to drop a file type.", + "example": [ + "**/tests/**", + "vendor/", + "*.md" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string", + "enum": [ + "connected" + ] + }, + "url": { + "type": "string", + "description": "Repository URL", + "example": "https://github.com/owner/repo" + }, + "branch": { + "type": "string", + "description": "Branch name, tag name, or commit SHA" + }, + "ref_type": { + "type": "string", + "enum": [ + "branch", + "tag", + "sha" + ], + "description": "How `branch` was selected (UI hint for draft re-use)" + }, + "github_installation_id": { + "type": "number", + "description": "GitHub App installation id. Omitted for PAT-based connections (e.g. GitHub Enterprise), where the installation is resolved from the repository URL and the organization instead." + } + }, + "required": [ + "source", + "url", + "branch" + ] + }, + "PublicRepoInputDto": { + "type": "object", + "properties": { + "include_paths": { + "description": "Whitebox scope: gitignore-style globs (relative to the repo root) that narrow the scan to matching paths. Empty means the whole repository.", + "example": [ + "services/gateway/**" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "exclude_paths": { + "description": "Whitebox scope: gitignore-style globs removed from the scan after include_paths is applied; use `*.md` to drop a file type.", + "example": [ + "**/tests/**", + "vendor/", + "*.md" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string", + "enum": [ + "public" + ] + }, + "url": { + "type": "string", + "description": "Repository URL", + "example": "https://github.com/owner/repo" + }, + "branch": { + "type": "string", + "description": "Branch name, tag name, or commit SHA" + }, + "ref_type": { + "type": "string", + "enum": [ + "branch", + "tag", + "sha" + ], + "description": "How `branch` was selected (UI hint for draft re-use)" + } + }, + "required": [ + "source", + "url", + "branch" + ] + }, + "UploadRepoInputDto": { + "type": "object", + "properties": { + "include_paths": { + "description": "Whitebox scope: gitignore-style globs (relative to the repo root) that narrow the scan to matching paths. Empty means the whole repository.", + "example": [ + "services/gateway/**" + ], + "type": "array", + "items": { + "type": "string" } }, + "exclude_paths": { + "description": "Whitebox scope: gitignore-style globs removed from the scan after include_paths is applied; use `*.md` to drop a file type.", + "example": [ + "**/tests/**", + "vendor/", + "*.md" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string", + "enum": [ + "upload" + ] + }, + "archive_id": { + "type": "string", + "description": "Scan archive ID" + } + }, + "required": [ + "source", + "archive_id" + ] + }, + "RestLoginTargetDto": { + "type": "object", + "properties": { + "site": { + "type": "string", + "description": "A deployed copy of the app to test signed in, e.g. https://staging.acme.dev", + "maxLength": 2048 + }, + "login_instructions": { + "type": "string", + "description": "How to sign in: the login page, username, password and any steps. Stored encrypted and never returned.", + "maxLength": 2000 + } + }, + "required": [ + "site", + "login_instructions" + ] + }, + "RestCreatePentestDto": { + "type": "object", + "properties": { "target_urls": { "description": "Target URLs (pentest only)", "type": "array", @@ -3235,21 +3213,16 @@ "description": "iva application this scan targets (group of repos)" }, "login_targets": { - "description": "Deployed sites to test signed in, each with how to log in, replacing any saved ones ([] removes them). A pentest tests live sites either all signed in or all without a login (target_urls). Check each login with POST /rest/pentests/{id}/login-checks before starting.", + "description": "Deployed sites to test signed in, each with how to sign in. On update the list replaces the saved one, and [] removes them. A pentest tests live sites either all signed in or all without a login (target_urls). Check each login with POST /pentests/{id}/login-checks before starting.", "maxItems": 10, "type": "array", "items": { "$ref": "#/components/schemas/RestLoginTargetDto" } - } - } - }, - "RestUpdatePentestDto": { - "type": "object", - "properties": { + }, "repos": { "type": "array", - "description": "Multiple repositories (takes precedence over repo_url/branch)", + "description": "Repositories or uploaded archives to test", "items": { "oneOf": [ { @@ -3270,8 +3243,199 @@ "upload": "#/components/schemas/UploadRepoInputDto" } } + }, + "minItems": 1 + } + }, + "required": [ + "repos" + ] + }, + "RepoDataDto": { + "type": "object", + "properties": { + "include_paths": { + "description": "Whitebox scope: gitignore-style globs (relative to the repo root) that narrow the scan to matching paths. Empty means the whole repository.", + "example": [ + "services/gateway/**" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "exclude_paths": { + "description": "Whitebox scope: gitignore-style globs removed from the scan after include_paths is applied; use `*.md` to drop a file type.", + "example": [ + "**/tests/**", + "vendor/", + "*.md" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "source": { + "type": "string", + "enum": [ + "connected", + "public", + "upload" + ] + }, + "provider": { + "type": "string", + "enum": [ + "github", + "gitlab", + "bitbucket", + "azure" + ], + "readOnly": true, + "description": "Resolved by the server at scan start; ignored on input" + }, + "url": { + "type": "string", + "description": "Repository URL", + "example": "https://github.com/owner/repo" + }, + "branch": { + "type": "string", + "description": "Branch name, tag name, or commit SHA" + }, + "ref_type": { + "type": "string", + "enum": [ + "branch", + "tag", + "sha" + ], + "description": "How `branch` was selected (UI hint for draft re-use)" + }, + "archive_id": { + "type": "string", + "description": "Scan archive ID" + }, + "github_installation_id": { + "type": "number", + "description": "GitHub App installation ID (connected repos only)" + } + } + }, + "RestPentestLoginTargetDto": { + "type": "object", + "properties": { + "site": { + "type": "string" + }, + "login_check": { + "type": "string", + "enum": [ + "unverified", + "pending", + "running", + "succeeded", + "failed", + "cancelled", + "expired" + ], + "description": "Whether Hacktron could sign in with the login the site has now. A pass lasts an hour, and changing the site or login makes it unverified." + }, + "failure_reason": { + "type": "string", + "nullable": true, + "description": "Why the check failed, without credentials" + } + }, + "required": [ + "site", + "login_check", + "failure_reason" + ] + }, + "RestPentestResponseDto": { + "type": "object", + "properties": { + "scan_id": { + "type": "string", + "format": "uuid", + "description": "Stable pentest id; task_id changes when it starts" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "running", + "completed", + "failed", + "stopped", + "cancelled", + "skipped", + "pending_verification", + "pending_triage", + "draft", + "estimating", + "estimated" + ] + }, + "repos": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RepoDataDto" } }, + "target_urls": { + "type": "array", + "items": { + "type": "string" + } + }, + "login_targets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RestPentestLoginTargetDto" + } + }, + "custom_context": { + "type": "string", + "nullable": true + }, + "application_id": { + "type": "string", + "nullable": true, + "format": "uuid" + }, + "cost_estimation_id": { + "type": "string", + "nullable": true, + "format": "uuid" + }, + "triage_enabled": { + "type": "boolean", + "nullable": true + }, + "credits_used": { + "type": "number", + "nullable": true + } + }, + "required": [ + "scan_id", + "status", + "repos", + "target_urls", + "login_targets", + "custom_context", + "application_id", + "cost_estimation_id", + "triage_enabled", + "credits_used" + ] + }, + "RestUpdatePentestDto": { + "type": "object", + "properties": { "target_urls": { "description": "Target URLs (pentest only)", "type": "array", @@ -3288,12 +3452,38 @@ "description": "iva application this scan targets (group of repos)" }, "login_targets": { - "description": "Deployed sites to test signed in, each with how to log in, replacing any saved ones ([] removes them). A pentest tests live sites either all signed in or all without a login (target_urls). Check each login with POST /rest/pentests/{id}/login-checks before starting.", + "description": "Deployed sites to test signed in, each with how to sign in. On update the list replaces the saved one, and [] removes them. A pentest tests live sites either all signed in or all without a login (target_urls). Check each login with POST /pentests/{id}/login-checks before starting.", "maxItems": 10, "type": "array", "items": { "$ref": "#/components/schemas/RestLoginTargetDto" } + }, + "repos": { + "type": "array", + "description": "Repositories or uploaded archives to test, replacing the saved ones. Changing them clears the estimate, so estimate again before starting.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ConnectedRepoInputDto" + }, + { + "$ref": "#/components/schemas/PublicRepoInputDto" + }, + { + "$ref": "#/components/schemas/UploadRepoInputDto" + } + ], + "discriminator": { + "propertyName": "source", + "mapping": { + "connected": "#/components/schemas/ConnectedRepoInputDto", + "public": "#/components/schemas/PublicRepoInputDto", + "upload": "#/components/schemas/UploadRepoInputDto" + } + } + }, + "minItems": 1 } } }, @@ -3301,7 +3491,7 @@ "type": "object", "properties": { "expected_credits": { - "type": "number", + "type": "integer", "description": "The total the user confirmed: the estimate's total_credits, plus triage_fee_credits when triage is required or opted in", "minimum": 1 }, From 2a754b4070de15778174ee600f20a9a6f92c8a88 Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Thu, 29 Oct 2026 15:31:41 +0000 Subject: [PATCH 03/10] docs: teammates can mention @Hacktron in a shared thread Co-Authored-By: Claude Opus 5.5 --- platform/communication-apps/slack-agent.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/communication-apps/slack-agent.mdx b/platform/communication-apps/slack-agent.mdx index f209043..80eb741 100644 --- a/platform/communication-apps/slack-agent.mdx +++ b/platform/communication-apps/slack-agent.mdx @@ -3,7 +3,7 @@ title: "Ask Hacktron in Slack" description: "Mention @Hacktron to ask about findings and set up pentests from Slack." --- -Mention **@Hacktron** in a channel, or send it a direct message, to ask about your scans and findings or to set up a pentest. It replies in a thread. Keep talking in that thread without mentioning it again. +Mention **@Hacktron** in a channel, or send it a direct message, to ask about your scans and findings or to set up a pentest. It replies in a thread. Keep talking in that thread without mentioning it again. Teammates can join the thread by mentioning @Hacktron, and it acts for each person with their own access. ## Before you start From a3b3d9bb3f89fca082dd80eba20a7f1677eb2dab Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Thu, 29 Oct 2026 16:58:58 +0000 Subject: [PATCH 04/10] docs: PR review settings over the API, MCP and Slack, and docs answers in Slack Co-Authored-By: Claude Opus 5.5 --- .../repos/get-pr-review-settings.mdx | 16 + api-reference/repos/set-pr-reviews.mdx | 13 + .../repos/update-pr-review-settings.mdx | 13 + docs.json | 7 +- mcp/get-started.mdx | 8 + openapi.json | 321 +++++++++++++++++- platform/communication-apps/slack-agent.mdx | 12 +- 7 files changed, 386 insertions(+), 4 deletions(-) create mode 100644 api-reference/repos/get-pr-review-settings.mdx create mode 100644 api-reference/repos/set-pr-reviews.mdx create mode 100644 api-reference/repos/update-pr-review-settings.mdx diff --git a/api-reference/repos/get-pr-review-settings.mdx b/api-reference/repos/get-pr-review-settings.mdx new file mode 100644 index 0000000..a910817 --- /dev/null +++ b/api-reference/repos/get-pr-review-settings.mdx @@ -0,0 +1,16 @@ +--- +title: "Get PR review settings" +openapi: "GET /pr-review-settings" +description: "Read how PR reviews behave across your organization." +--- + +Returns your organization's PR review settings, the ones under **Settings** in Hacktron: + +- `scm_comments_enabled`: reviews post comments and finding details on pull and merge requests. +- `scm_system_comments_enabled`: reviews also post system comments, such as skipped reviews, usage limits and configuration warnings. +- `fail_on_severity`: the severity at or above which a finding fails the PR check, or `null` when it never fails. +- `scan_include_*` and `scan_exclude_*`: which pull requests are reviewed, by target branch, label, author and title keyword. `null` means no filter. + +A repository's [`.hacktron/config.yaml`](/code-review/config) overrides these. Whether each repository has reviews turned on is `prScansEnabled` in [List repositories](/api-reference/repos/list-repos). + +**Scope required**: `read` diff --git a/api-reference/repos/set-pr-reviews.mdx b/api-reference/repos/set-pr-reviews.mdx new file mode 100644 index 0000000..88f59bb --- /dev/null +++ b/api-reference/repos/set-pr-reviews.mdx @@ -0,0 +1,13 @@ +--- +title: "Turn PR reviews on or off" +openapi: "POST /repos/pr-reviews" +description: "Turn automatic pull and merge request reviews on or off for repositories." +--- + +Turns automatic PR and MR reviews on for the repositories in `enable` and off for those in `disable`. Use each repository's `id` from [List repositories](/api-reference/repos/list-repos), whichever provider it's on. Up to 100 of each per request. + +**Scope required**: `write`. **Role**: organization admin. + +- A repository must be enabled for Hacktron access before its reviews can be turned on. Enable it from **Repositories** in Hacktron. +- One repository failing doesn't stop the rest. The response counts the repositories that changed (`processed`) and lists each one that didn't with the reason (`failedRepositories`). +- `webhookPending` lists repositories that were turned on but whose provider webhook couldn't be registered yet. Reviews start once it is. diff --git a/api-reference/repos/update-pr-review-settings.mdx b/api-reference/repos/update-pr-review-settings.mdx new file mode 100644 index 0000000..6cb5acb --- /dev/null +++ b/api-reference/repos/update-pr-review-settings.mdx @@ -0,0 +1,13 @@ +--- +title: "Update PR review settings" +openapi: "PATCH /pr-review-settings" +description: "Change how PR reviews behave across your organization." +--- + +Changes your organization's PR review settings and returns all of them. Only the fields you send change. Send `null` to turn off `fail_on_severity` or clear a filter. + +**Scope required**: `write`. **Role**: organization admin. + +A filter is replaced as a whole. To add one branch, read the settings with [Get PR review settings](/api-reference/repos/get-pr-review-settings) first and send the full list. + +Branch and author filters take glob patterns (`*`, `**`, `?`, `{a,b}`) and are case-insensitive. Each filter holds up to 50 entries. diff --git a/docs.json b/docs.json index 3a74ccc..a38ae60 100644 --- a/docs.json +++ b/docs.json @@ -222,7 +222,12 @@ }, { "group": "Repositories", - "pages": ["api-reference/repos/list-repos"] + "pages": [ + "api-reference/repos/list-repos", + "api-reference/repos/set-pr-reviews", + "api-reference/repos/get-pr-review-settings", + "api-reference/repos/update-pr-review-settings" + ] }, { "group": "Dependency Firewall", diff --git a/mcp/get-started.mdx b/mcp/get-started.mdx index 402bb39..47f3fd0 100644 --- a/mcp/get-started.mdx +++ b/mcp/get-started.mdx @@ -203,6 +203,14 @@ Each tool calls the same endpoint as the [REST API](/api-reference/introduction) | `start_pentest` | Start a pentest and spend its credits (admins only) | [`POST /pentests/{id}/start`](/api-reference/pentests/start-pentest) | | `discard_pentest_draft` | Delete a pentest that hasn't started | [`DELETE /pentests/{id}`](/api-reference/pentests/discard-pentest) | +### PR reviews + +| Name | Description | REST endpoint | +| --------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `get_pr_review_settings` | Get how PR reviews behave across the organisation | [`GET /pr-review-settings`](/api-reference/repos/get-pr-review-settings) | +| `update_pr_review_settings` | Change PR review comments, fail-on severity and filters (admins only) | [`PATCH /pr-review-settings`](/api-reference/repos/update-pr-review-settings) | +| `set_pr_reviews` | Turn PR reviews on or off for repositories (admins only) | [`POST /repos/pr-reviews`](/api-reference/repos/set-pr-reviews) | + To test a site signed in, your assistant sends the login details to Hacktron, where they're stored encrypted. Anything you type in the conversation also diff --git a/openapi.json b/openapi.json index 72bc56b..5263c37 100644 --- a/openapi.json +++ b/openapi.json @@ -1517,6 +1517,120 @@ "REST API - Credits" ] } + }, + "/repos/pr-reviews": { + "post": { + "operationId": "RestRepoController_setPrReviews", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryIdBatchDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RepositoryBatchResultDto" + } + } + } + } + }, + "summary": "Turn PR/MR reviews on or off for repositories, by their id from the repository list. A repository must be selected for Hacktron access before its reviews can be turned on.", + "tags": [ + "REST API - Repos" + ] + } + }, + "/pr-review-settings": { + "get": { + "operationId": "RestPrReviewSettingsController_get", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPrReviewSettingsDto" + } + } + } + } + }, + "summary": "Get the organization's PR/MR review settings: whether reviews post comments, the severity that fails the PR check, and which PRs are reviewed", + "tags": [ + "REST API - Repos" + ] + }, + "patch": { + "operationId": "RestPrReviewSettingsController_update", + "parameters": [ + { + "name": "X-Organization-Id", + "in": "header", + "description": "Organization ID", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPrReviewSettingsDto" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestPrReviewSettingsDto" + } + } + } + } + }, + "summary": "Change the organization's PR/MR review settings. Only the fields sent change; null turns off the fail-on severity or clears a filter. A repository's .hacktron/config.yaml overrides these.", + "tags": [ + "REST API - Repos" + ] + } } }, "info": { @@ -3615,6 +3729,211 @@ "used_this_period", "is_unlimited" ] + }, + "RepositoryBatchResultDto": { + "type": "object", + "properties": { + "processed": { + "type": "number", + "description": "Keys applied", + "example": 2 + }, + "failed": { + "type": "number", + "description": "Keys that failed", + "example": 0 + }, + "failedRepositories": { + "description": "Keys that failed, with the reason", + "type": "array", + "items": { + "$ref": "#/components/schemas/RepositoryBatchFailureDto" + } + }, + "webhookPending": { + "description": "Keys turned on whose provider webhook could not be registered", + "example": [], + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "processed", + "failed", + "failedRepositories", + "webhookPending" + ] + }, + "RepositoryBatchFailureDto": { + "type": "object", + "properties": { + "repo": { + "type": "string", + "description": "The key the request named", + "example": "acme/api" + }, + "reason": { + "type": "string", + "description": "Why the key was not applied", + "example": "Repository acme/api not found" + } + }, + "required": [ + "repo", + "reason" + ] + }, + "RepositoryIdBatchDto": { + "type": "object", + "properties": { + "enable": { + "description": "Ids from the repository list of the repositories to turn on", + "example": [ + "123e4567-e89b-12d3-a456-426614174000" + ], + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "disable": { + "description": "Ids from the repository list of the repositories to turn off", + "example": [], + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + "required": [ + "enable", + "disable" + ] + }, + "RestPrReviewSettingsDto": { + "type": "object", + "properties": { + "scm_comments_enabled": { + "type": "boolean", + "description": "Whether Hacktron publishes comments and finding details to connected source control pull and merge requests" + }, + "scm_system_comments_enabled": { + "type": "boolean", + "description": "Whether Hacktron posts system comments such as skipped reviews, usage limits, and configuration warnings. Defaults to true. Requires scm_comments_enabled." + }, + "fail_on_severity": { + "type": "string", + "description": "Org-wide default fail_on severity threshold for PR/MR checks. A repo's .hacktron/config.yaml fail_on.severity overrides this. Send null to disable.", + "enum": [ + "critical", + "high", + "medium", + "low", + "info" + ], + "nullable": true + }, + "scan_include_branches": { + "description": "Org-wide default target branch patterns to scan PRs/MRs for (null = all branches). Glob patterns (`*`, `**`, `?`, `{a,b}`), case-insensitive. A repo's .hacktron/config.yaml include.branches overrides this.", + "example": [ + "main", + "**/release/**" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_exclude_branches": { + "description": "Org-wide default target branch patterns to exclude from PR/MR scans (null = none). Takes precedence over scan_include_branches. A repo's .hacktron/config.yaml exclude.branches overrides this.", + "example": [ + "**/release/legacy/**" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_include_labels": { + "description": "Org-wide default labels to require on PRs/MRs for scanning (null = any). A repo's .hacktron/config.yaml include.labels overrides this.", + "example": [ + "feature", + "bugfix" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_exclude_labels": { + "description": "Org-wide default labels to exclude PR/MR scans for (null = none). Takes precedence over scan_include_labels. A repo's .hacktron/config.yaml exclude.labels overrides this.", + "example": [ + "wip", + "no-scan" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_include_authors": { + "description": "Org-wide default author patterns to require on PRs/MRs for scanning (null = any). Glob patterns (`*`, `**`, `?`, `{a,b}`), case-insensitive. A repo's .hacktron/config.yaml include.authors overrides this.", + "example": [ + "alice", + "bob" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_exclude_authors": { + "description": "Org-wide default author patterns to exclude PR/MR scans for (null = none). Glob patterns (`*`, `**`, `?`, `{a,b}`), case-insensitive. Takes precedence over scan_include_authors. A repo's .hacktron/config.yaml exclude.authors overrides this.", + "example": [ + "dependabot[bot]", + "renovate[bot]", + "*-bot" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_include_keywords": { + "description": "Org-wide default title keywords to require on PRs/MRs for scanning (null = any). Case-insensitive substring match. A repo's .hacktron/config.yaml include.keywords overrides this.", + "example": [ + "release", + "security" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + }, + "scan_exclude_keywords": { + "description": "Org-wide default title keywords to exclude PR/MR scans for (null = none). Case-insensitive substring match. A repo's .hacktron/config.yaml exclude.keywords overrides this.", + "example": [ + "[exclude hacktron]", + "wip" + ], + "nullable": true, + "type": "array", + "items": { + "type": "string" + } + } + } } } }, @@ -3623,4 +3942,4 @@ "api-key": [] } ] -} \ No newline at end of file +} diff --git a/platform/communication-apps/slack-agent.mdx b/platform/communication-apps/slack-agent.mdx index 80eb741..e35c742 100644 --- a/platform/communication-apps/slack-agent.mdx +++ b/platform/communication-apps/slack-agent.mdx @@ -1,9 +1,9 @@ --- title: "Ask Hacktron in Slack" -description: "Mention @Hacktron to ask about findings and set up pentests from Slack." +description: "Mention @Hacktron to ask about findings, set up pentests and manage PR reviews from Slack." --- -Mention **@Hacktron** in a channel, or send it a direct message, to ask about your scans and findings or to set up a pentest. It replies in a thread. Keep talking in that thread without mentioning it again. Teammates can join the thread by mentioning @Hacktron, and it acts for each person with their own access. +Mention **@Hacktron** in a channel, or send it a direct message, to ask about your scans and findings, set up a pentest, turn PR reviews on or off, or ask how Hacktron works. It replies in a thread. Keep talking in that thread without mentioning it again. Teammates can join the thread by mentioning @Hacktron, and it acts for each person with their own access. ## Before you start @@ -21,6 +21,14 @@ Ask in your own words, for example: Hacktron answers with counts and posts findings as cards that link to them in Hacktron. +## Ask how Hacktron works + +Ask a question about the product, such as "How do I stop reviews of PRs into release branches?". Hacktron answers from these docs and links the page it used. When the docs don't cover it, it says so. + +## Manage PR reviews + +Ask Hacktron to turn PR reviews on or off for a repository, or to change how they behave across your organization: whether they comment on pull requests, the severity that fails the PR check, and which branches, labels, authors or title keywords get reviewed. Anyone can ask what the settings are. Only organization admins can change them, and Hacktron shows an **Approve** button before it does. + ## Set up a pentest From 67703dd07888a1418c735261714a9ce1ac49b006 Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Thu, 29 Oct 2026 18:00:34 +0000 Subject: [PATCH 05/10] docs: how to install Hacktron AI in Slack and add it to channels Step-by-step setup for the Slack agent: an admin connects Slack, everyone links their Slack account, add Hacktron AI to a channel or DM it, and connect your Hacktron account on first mention. Adds troubleshooting and points the Slack integration page at the agent setup. Co-Authored-By: Claude Opus 5.5 --- code-review/integrations/slack.mdx | 2 +- platform/communication-apps/slack-agent.mdx | 52 ++++++++++++++++++--- platform/communication-apps/slack.mdx | 9 +++- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/code-review/integrations/slack.mdx b/code-review/integrations/slack.mdx index a3884d1..338b892 100644 --- a/code-review/integrations/slack.mdx +++ b/code-review/integrations/slack.mdx @@ -61,7 +61,7 @@ so that Hacktron can recognize you on Slack. -You can now provide feedback and act on findings directly from Slack. +You can now provide feedback and act on findings directly from Slack, and [ask Hacktron in Slack](/platform/communication-apps/slack-agent) as yourself. + + In Hacktron, go to **Integrations**, find **Slack** under **Communication + Apps** and click **Connect**. Approve the Hacktron AI app for your + workspace. This installs the app in Slack. See + [Slack](/platform/communication-apps/slack) for the full steps. + + + If your organization connected Slack before Hacktron could read files, + click **Connect** again so Hacktron can read archives people attach. + + + + + In Hacktron, open **Account**, scroll to **Connected Accounts** and click + **Connect** next to **Slack**. Choose the same workspace. This is how + Hacktron knows who's asking. See [Link your Slack + identity](/code-review/integrations/slack#link-your-slack-identity). + + + In Slack, open the channel and send `/invite @Hacktron AI`. You can also + click the channel name, open **Integrations** and click **Add apps**. This + works for public and private channels. + + To talk to it privately instead, find **Hacktron AI** under **Apps** in + the Slack sidebar and send it a message. + + + + Mention **@Hacktron AI** with a question. The first time, Hacktron sends + you a private **Connect Hacktron** message that only you can see. Click + it, sign in to Hacktron and allow access. The thread says it's waiting for + you, then Hacktron carries on with your question. + + + +Hacktron acts as you, with your role: viewers can ask questions, members can set up pentests, and organization admins can start them and change settings. ## Ask about findings @@ -79,3 +113,9 @@ While it works, Hacktron shows its plan and each step it takes in a progress car - Hacktron reads attached archives to understand the code, but never runs your code. To test the running app, give it a deployed site. - Each thread has a usage limit. When a thread reaches it, Hacktron stops and asks you to mention it in a new thread. - If Hacktron can't read files you attach, ask an organization admin to reconnect Slack from **Integrations** so the app gets permission to read files. + +## Troubleshooting + +- **Hacktron doesn't reply to a mention in a channel.** Add it to the channel with `/invite @Hacktron AI`, and check that Slack is connected under **Integrations** in Hacktron. +- **"Hacktron couldn't find your account for this workspace."** Link your Slack account from **Account** > **Connected Accounts** in Hacktron, choosing the same workspace, then try again. +- **A reply without a mention gets no answer.** Only the person who started the thread can reply without mentioning Hacktron. Everyone else mentions **@Hacktron AI** in the thread. diff --git a/platform/communication-apps/slack.mdx b/platform/communication-apps/slack.mdx index 92af57f..a369fd8 100644 --- a/platform/communication-apps/slack.mdx +++ b/platform/communication-apps/slack.mdx @@ -28,7 +28,8 @@ You also need permission to authorize apps in the Slack workspace you want Hackt Hacktron uses this access to send security notifications and scan updates - to the Slack channel you configure. + to the Slack channel you configure, and to answer people who mention + @Hacktron AI. @@ -61,10 +62,14 @@ Open the Slack card in **Integrations** and choose **Disconnect**. Hacktron will - If a channel is missing, confirm that the Slack workspace account used for OAuth can access that channel. - If notifications are not appearing, confirm that the Slack integration is connected and a channel is selected. +## Next: ask Hacktron in Slack + +Connecting Slack installs the Hacktron AI app in your workspace. To let people ask it questions, set up pentests and manage PR reviews, add it to channels with `/invite @Hacktron AI` and have each person link their Slack account. + - Mention @Hacktron to ask about findings and set up pentests. + Set up and use @Hacktron AI in your channels. From df595ccb93aabc5c535c27982f3ff5522a5e8009 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2026 01:19:13 +0000 Subject: [PATCH 06/10] docs: frame Slack agent as Hacktron AI assistant and give it its own nav group Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- code-review/integrations/slack.mdx | 2 +- docs.json | 7 +++-- platform/communication-apps/slack.mdx | 11 +++---- .../slack-agent.mdx => slack-assistant.mdx} | 30 +++++++++++++++++-- white-box-pentest/quickstart.mdx | 4 +-- 5 files changed, 41 insertions(+), 13 deletions(-) rename platform/{communication-apps/slack-agent.mdx => slack-assistant.mdx} (72%) diff --git a/code-review/integrations/slack.mdx b/code-review/integrations/slack.mdx index 338b892..d92d97b 100644 --- a/code-review/integrations/slack.mdx +++ b/code-review/integrations/slack.mdx @@ -61,7 +61,7 @@ so that Hacktron can recognize you on Slack. -You can now provide feedback and act on findings directly from Slack, and [ask Hacktron in Slack](/platform/communication-apps/slack-agent) as yourself. +You can now provide feedback and act on findings directly from Slack, and [mention @Hacktron AI](/platform/slack-assistant) to get work done in Hacktron as yourself. - Set up and use @Hacktron AI in your channels. + Triage findings, set up pentests and manage PR reviews by mentioning @Hacktron + AI. diff --git a/platform/communication-apps/slack-agent.mdx b/platform/slack-assistant.mdx similarity index 72% rename from platform/communication-apps/slack-agent.mdx rename to platform/slack-assistant.mdx index b30db01..5e73281 100644 --- a/platform/communication-apps/slack-agent.mdx +++ b/platform/slack-assistant.mdx @@ -1,9 +1,21 @@ --- -title: "Ask Hacktron in Slack" -description: "Mention @Hacktron AI to ask about findings, set up pentests and manage PR reviews from Slack." +title: "Hacktron AI in Slack" +description: "Mention @Hacktron AI to triage findings, set up pentests and change PR review settings without leaving Slack." --- -Mention **@Hacktron AI** in a channel, or send it a direct message, to ask about your scans and findings, set up a pentest, turn PR reviews on or off, or ask how Hacktron works. It replies in a thread. Keep talking in that thread without mentioning it again. Teammates can join the thread by mentioning @Hacktron AI, and it acts for each person with their own access. +Hacktron AI is an assistant that works in your Slack channels and DMs. Mention **@Hacktron AI** with a request and it does the work in Hacktron as you, with your access, and replies in a thread. Keep talking in that thread without mentioning it again. Teammates can join the thread by mentioning @Hacktron AI, and it acts for each person with their own access. + +## What you can ask it to do + +| Task | Try | +| ------------------------------------------------- | ------------------------------------------------------------------------------- | +| [Look into findings](#ask-about-findings) | "How many critical findings are open in acme/api?" | +| [Triage a finding](#act-on-findings-and-scans) | "Mark the SSRF finding in acme/api as a false positive, the URL is allowlisted" | +| [Set up a pentest](#set-up-a-pentest) | "Pentest acme/api on main and test staging.acme.com signed in" | +| [Manage PR reviews](#manage-pr-reviews) | "Turn on PR reviews for acme/api and acme/web" | +| [Ask how Hacktron works](#ask-how-hacktron-works) | "How do I stop reviews of PRs into release branches?" | + +Before Hacktron changes a finding, a scan or your PR review settings, it shows you an **Approve** button that says what will change. Pentests start only when an organization admin approves them. ## Set up @@ -25,6 +37,12 @@ Mention **@Hacktron AI** in a channel, or send it a direct message, to ask about **Connect** next to **Slack**. Choose the same workspace. This is how Hacktron knows who's asking. See [Link your Slack identity](/code-review/integrations/slack#link-your-slack-identity). + + Slack under Connected Accounts in Hacktron + In Slack, open the channel and send `/invite @Hacktron AI`. You can also @@ -55,6 +73,12 @@ Ask in your own words, for example: Hacktron answers with counts and posts findings as cards that link to them in Hacktron. +## Act on findings and scans + +Ask Hacktron to change a finding's status, add a comment to a finding, stop a running scan or discard a pentest draft. It shows you an **Approve** button that says what will change, then confirms what it did in the thread. + +Each finding card also has buttons to update its status or fix it with AI. + ## Ask how Hacktron works Ask a question about the product, such as "How do I stop reviews of PRs into release branches?". Hacktron answers from these docs and links the page it used. When the docs don't cover it, it says so. diff --git a/white-box-pentest/quickstart.mdx b/white-box-pentest/quickstart.mdx index f10f1f4..1183998 100644 --- a/white-box-pentest/quickstart.mdx +++ b/white-box-pentest/quickstart.mdx @@ -206,9 +206,9 @@ Only organization admins can start a pentest. Mention @Hacktron with a repository or an attached archive. From cc746f6a3cd432c39b7bb750599e685ee5ca90b3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2026 01:22:42 +0000 Subject: [PATCH 07/10] docs: retitle to Hacktron in Slack Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- platform/communication-apps/slack.mdx | 8 ++------ platform/slack-assistant.mdx | 2 +- white-box-pentest/quickstart.mdx | 6 +----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/platform/communication-apps/slack.mdx b/platform/communication-apps/slack.mdx index 397ce7c..857f6d7 100644 --- a/platform/communication-apps/slack.mdx +++ b/platform/communication-apps/slack.mdx @@ -62,15 +62,11 @@ Open the Slack card in **Integrations** and choose **Disconnect**. Hacktron will - If a channel is missing, confirm that the Slack workspace account used for OAuth can access that channel. - If notifications are not appearing, confirm that the Slack integration is connected and a channel is selected. -## Next: use Hacktron AI in Slack +## Next: use Hacktron in Slack Connecting Slack installs the Hacktron AI app in your workspace. To let people triage findings, set up pentests and manage PR reviews from Slack, add it to channels with `/invite @Hacktron AI` and have each person link their Slack account. - + Triage findings, set up pentests and manage PR reviews by mentioning @Hacktron AI. diff --git a/platform/slack-assistant.mdx b/platform/slack-assistant.mdx index 5e73281..ab9e27c 100644 --- a/platform/slack-assistant.mdx +++ b/platform/slack-assistant.mdx @@ -1,5 +1,5 @@ --- -title: "Hacktron AI in Slack" +title: "Hacktron in Slack" description: "Mention @Hacktron AI to triage findings, set up pentests and change PR review settings without leaving Slack." --- diff --git a/white-box-pentest/quickstart.mdx b/white-box-pentest/quickstart.mdx index 1183998..b6e9a49 100644 --- a/white-box-pentest/quickstart.mdx +++ b/white-box-pentest/quickstart.mdx @@ -205,11 +205,7 @@ same steps as the dashboard: set the scope, estimate the credits, then start. Only organization admins can start a pentest. - + Mention @Hacktron with a repository or an attached archive. From 11acc11e8275dc4c4141fcf970769532dbce6d15 Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Sat, 31 Oct 2026 20:50:49 +0000 Subject: [PATCH 08/10] docs: clarify customer-facing Slack, MCP and API guides --- .../archives/request-archive-upload.mdx | 10 +-- api-reference/findings/count-findings.mdx | 2 +- .../pentests/check-pentest-logins.mdx | 12 +-- api-reference/pentests/create-pentest.mdx | 10 +-- api-reference/pentests/estimate-pentest.mdx | 2 +- api-reference/pentests/start-pentest.mdx | 2 +- api-reference/pentests/update-pentest.mdx | 8 +- .../repos/get-pr-review-settings.mdx | 4 +- api-reference/repos/set-pr-reviews.mdx | 8 +- .../repos/update-pr-review-settings.mdx | 4 +- api-reference/scans/stop-scan.mdx | 2 +- code-review/integrations/slack.mdx | 2 +- mcp/get-started.mdx | 60 +++++++------- platform/communication-apps/slack.mdx | 6 +- platform/slack-assistant.mdx | 80 ++++++++++--------- white-box-pentest/quickstart.mdx | 12 +-- 16 files changed, 116 insertions(+), 108 deletions(-) diff --git a/api-reference/archives/request-archive-upload.mdx b/api-reference/archives/request-archive-upload.mdx index 11752e9..6377d69 100644 --- a/api-reference/archives/request-archive-upload.mdx +++ b/api-reference/archives/request-archive-upload.mdx @@ -4,17 +4,17 @@ openapi: "POST /archives/uploads" description: "Get a URL to upload a code archive to, for code that isn't in a connected repository." --- -Returns a signed URL to upload a `.zip`, `.7z` or `.tar.gz` archive of source code to. +Returns a signed URL for uploading a `.zip`, `.7z` or `.tar.gz` source archive. **Scope required**: `write`. **Role**: member or above. 1. Call this endpoint with the archive's `file_name`. 2. `PUT` the file to `upload_url`, with the `Content-Type` header set to the returned `content_type`. -3. Pentest it by adding `{ "source": "upload", "archive_id": "" }` to `repos` in [Create pentest draft](/api-reference/pentests/create-pentest). +3. Add `{ "source": "upload", "archive_id": "" }` to `repos` when you [create a pentest draft](/api-reference/pentests/create-pentest). -To pentest only part of the archive, set `include_paths` or `exclude_paths` on that repo instead of uploading a smaller archive. +To assess part of the archive, set `include_paths` or `exclude_paths` on its entry in `repos`. You can use the same uploaded archive. - Some self-hosted installs don't support uploads through the API and return - `400`. Upload the archive in the Hacktron web app instead. + Some self-hosted deployments return `400` because they don't support API + uploads. In that case, upload the archive through the Hacktron web app. diff --git a/api-reference/findings/count-findings.mdx b/api-reference/findings/count-findings.mdx index fc97010..a1ebdb2 100644 --- a/api-reference/findings/count-findings.mdx +++ b/api-reference/findings/count-findings.mdx @@ -4,6 +4,6 @@ openapi: "GET /findings/counts" description: "Count findings by severity, state and repository." --- -Counts the findings that match the same filters as [List findings](/api-reference/findings/list-findings), without paging through them. The response has the total, counts for every severity and state (zero when there are none), and counts per repository with the most findings first. +Counts findings using the same filters as [List findings](/api-reference/findings/list-findings), without requiring you to fetch every page. The response includes the total and counts by severity, state and repository. Severity and state counts include zero values. Repositories appear in order of finding count, highest first. **Scope required**: `read` diff --git a/api-reference/pentests/check-pentest-logins.mdx b/api-reference/pentests/check-pentest-logins.mdx index bd2d7c7..7417ccb 100644 --- a/api-reference/pentests/check-pentest-logins.mdx +++ b/api-reference/pentests/check-pentest-logins.mdx @@ -4,12 +4,14 @@ openapi: "POST /pentests/{id}/login-checks" description: "Check that Hacktron can sign in to each login target before the pentest starts." --- -Has Hacktron sign in to each of the draft's `login_targets` with its login instructions. Every login must pass before the pentest can start. +Checks whether Hacktron can sign in to each of the draft's `login_targets` using the saved login instructions. Every login check must pass before the pentest can start. **Scope required**: `write`. **Role**: member or above. -- A passed check lasts one hour. Start the pentest within the hour, or check again. -- Logins that are already being checked, or that passed within the last hour, aren't checked again unless their site or login instructions changed. -- Set `wait_seconds` (up to 50) to wait for the checks to finish before the response returns. Without it, the response returns while the checks are `pending`. Call again with `wait_seconds` to wait for them. Each call checks failed logins again, so fix their instructions first. +- A successful check is valid for one hour. Start the pentest within that time or run the check again. +- Hacktron skips checks that are already running or passed within the last hour, unless the site or login instructions have changed. +- Set `wait_seconds` to wait up to 50 seconds for the checks. Without it, the endpoint returns while checks are `pending`. You can call again with `wait_seconds` to wait for the results. Each call retries failed logins, so update incorrect instructions before calling again. -Each entry in `login_targets` reports its `login_check`: `pending` or `running` while Hacktron signs in, `succeeded` when it signed in, and `failed` when it couldn't. A failed check has a `failure_reason`, which never includes the login details. Fix the instructions with [Update pentest draft](/api-reference/pentests/update-pentest) and check again. +Each entry in `login_targets` includes a `login_check` status: `pending` or `running` while the check is in progress, `succeeded` after a successful sign-in, or `failed` if Hacktron couldn't sign in. + +A failed check includes a `failure_reason` without login details. Correct the instructions with [Update pentest draft](/api-reference/pentests/update-pentest), then run the check again. diff --git a/api-reference/pentests/create-pentest.mdx b/api-reference/pentests/create-pentest.mdx index 434bb58..80c0f90 100644 --- a/api-reference/pentests/create-pentest.mdx +++ b/api-reference/pentests/create-pentest.mdx @@ -4,18 +4,18 @@ openapi: "POST /pentests" description: "Create a pentest that hasn't started, from repositories or uploaded archives, optionally with live sites." --- -Creates a draft pentest. A draft holds the code to review and, optionally, deployed sites to test live. Nothing runs and no credits are spent until you [start it](/api-reference/pentests/start-pentest). +Creates a draft with the code you want to review and any deployed sites you want to test. Creating a draft does not run a pentest or spend credits. You review the estimate before [starting it](/api-reference/pentests/start-pentest). **Scope required**: `write`. **Role**: member or above. -## Lifecycle +## Set up and start a pentest 1. Create the draft with this endpoint, and change it with [Update pentest draft](/api-reference/pentests/update-pentest). 2. If the draft has `login_targets`, run [Check pentest logins](/api-reference/pentests/check-pentest-logins) until each login passes. -3. Price it with [Estimate pentest](/api-reference/pentests/estimate-pentest). +3. Get the credit cost with [Estimate pentest](/api-reference/pentests/estimate-pentest). 4. Start it with [Start pentest](/api-reference/pentests/start-pentest), or delete it with [Discard pentest draft](/api-reference/pentests/discard-pentest). -Keep the `scan_id` from the response. It identifies the pentest from draft to finish, so you can follow it afterwards with [Get scan](/api-reference/scans/get-scan). +Save the `scan_id` from the response. Use the same ID throughout setup and after the pentest starts, including when calling [Get scan](/api-reference/scans/get-scan). ## What to test @@ -23,7 +23,7 @@ Keep the `scan_id` from the response. It identifies the pentest from draft to fi - `target_urls`: deployed sites to test without signing in. - `login_targets`: deployed sites to test signed in. Each has a `site` and `login_instructions`: the login page, username, password and any extra steps. Login instructions are stored encrypted and never returned. -A pentest tests its live sites either all signed in or all without a login, so one draft can't have both `target_urls` and `login_targets`. Create a separate pentest for the other kind. +A draft can have `target_urls` or `login_targets`, but not both. To test sites with and without a login, create separate pentests. Signed-in testing is enabled per organization. If it isn't enabled for yours, diff --git a/api-reference/pentests/estimate-pentest.mdx b/api-reference/pentests/estimate-pentest.mdx index a6734de..a880967 100644 --- a/api-reference/pentests/estimate-pentest.mdx +++ b/api-reference/pentests/estimate-pentest.mdx @@ -8,6 +8,6 @@ Starts a cost estimation for the draft and returns its `cost_estimation_id`. Pol **Scope required**: `write`. **Role**: member or above. -The total to confirm when you [start the pentest](/api-reference/pentests/start-pentest) is the estimate's `total_credits`, plus `triage_fee_credits` when `triage_required` is true or you opt in to triage. +When you [start the pentest](/api-reference/pentests/start-pentest), confirm the estimate's `total_credits`. Add `triage_fee_credits` if `triage_required` is true or you opt in to triage. Estimate again whenever you change the draft. diff --git a/api-reference/pentests/start-pentest.mdx b/api-reference/pentests/start-pentest.mdx index b687fa5..6efa2b9 100644 --- a/api-reference/pentests/start-pentest.mdx +++ b/api-reference/pentests/start-pentest.mdx @@ -8,7 +8,7 @@ Starts a draft pentest that has a completed estimate. Credits are deducted when **Scope required**: `write`. **Role**: organization admin. -- `expected_credits` is the total you confirmed: the estimate's `total_credits`, plus `triage_fee_credits` when triage is required or you set `triage_enabled`. If the pentest now costs a different amount, the request fails with `409` and nothing is spent. Without enough credits it fails with `402`. +- Set `expected_credits` to the estimate's `total_credits`, plus `triage_fee_credits` if triage is required or you set `triage_enabled`. If the current cost differs from this total, the endpoint returns `409` without spending credits. If the organization has insufficient credits, it returns `402`. - Every login target needs a login check that passed within the last hour. See [Check pentest logins](/api-reference/pentests/check-pentest-logins). After it starts, follow the pentest with [Get scan status](/api-reference/scans/get-scan-status) using the same `scan_id`, read its results with [List scan findings](/api-reference/scans/list-scan-findings), or stop it with [Stop scan](/api-reference/scans/stop-scan). diff --git a/api-reference/pentests/update-pentest.mdx b/api-reference/pentests/update-pentest.mdx index a0e1fbe..db00939 100644 --- a/api-reference/pentests/update-pentest.mdx +++ b/api-reference/pentests/update-pentest.mdx @@ -4,13 +4,13 @@ openapi: "PATCH /pentests/{id}" description: "Change a pentest that hasn't started." --- -Changes a draft pentest. Fields you send replace the saved values, and fields you leave out are kept. A pentest that has started can't be changed. +Updates a draft pentest. Fields in the request replace their saved values; omitted fields keep their current values. You can't change a pentest after it starts. **Scope required**: `write`. **Role**: member or above. -- `repos` replaces the saved repositories and needs at least one. Changing them clears the estimate. -- `login_targets` replaces the whole list, and `[]` removes every login target. Resending a site with the same login instructions keeps its passed login check. A changed site or login reports `login_check: unverified` until you check it again. +- `repos` replaces the saved repository list and must contain at least one entry. Changing it clears the estimate. +- `login_targets` replaces the full list. Send `[]` to remove all login targets. Sending the same site and login instructions preserves a successful login check. Changing either resets `login_check` to `unverified` until you check again. - To fix a login that failed its check, send the site again with new `login_instructions`, then run [Check pentest logins](/api-reference/pentests/check-pentest-logins). - To switch a draft from signed-in testing to testing without a login (or back), clear the other list in the same request, for example `"login_targets": []` with your `target_urls`. -Estimate the pentest again after changing it. [Start pentest](/api-reference/pentests/start-pentest) rejects a total that doesn't match the current estimate. +Run the estimate again after changing the draft. [Start pentest](/api-reference/pentests/start-pentest) rejects a credit total that doesn't match the current estimate. diff --git a/api-reference/repos/get-pr-review-settings.mdx b/api-reference/repos/get-pr-review-settings.mdx index a910817..471b857 100644 --- a/api-reference/repos/get-pr-review-settings.mdx +++ b/api-reference/repos/get-pr-review-settings.mdx @@ -4,13 +4,13 @@ openapi: "GET /pr-review-settings" description: "Read how PR reviews behave across your organization." --- -Returns your organization's PR review settings, the ones under **Settings** in Hacktron: +Returns the PR review settings configured for your organization under **Settings** in Hacktron: - `scm_comments_enabled`: reviews post comments and finding details on pull and merge requests. - `scm_system_comments_enabled`: reviews also post system comments, such as skipped reviews, usage limits and configuration warnings. - `fail_on_severity`: the severity at or above which a finding fails the PR check, or `null` when it never fails. - `scan_include_*` and `scan_exclude_*`: which pull requests are reviewed, by target branch, label, author and title keyword. `null` means no filter. -A repository's [`.hacktron/config.yaml`](/code-review/config) overrides these. Whether each repository has reviews turned on is `prScansEnabled` in [List repositories](/api-reference/repos/list-repos). +A repository's [`.hacktron/config.yaml`](/code-review/config) takes precedence over these defaults. To check whether reviews are enabled for a repository, read its `prScansEnabled` value from [List repositories](/api-reference/repos/list-repos). **Scope required**: `read` diff --git a/api-reference/repos/set-pr-reviews.mdx b/api-reference/repos/set-pr-reviews.mdx index 88f59bb..b8e7a14 100644 --- a/api-reference/repos/set-pr-reviews.mdx +++ b/api-reference/repos/set-pr-reviews.mdx @@ -4,10 +4,10 @@ openapi: "POST /repos/pr-reviews" description: "Turn automatic pull and merge request reviews on or off for repositories." --- -Turns automatic PR and MR reviews on for the repositories in `enable` and off for those in `disable`. Use each repository's `id` from [List repositories](/api-reference/repos/list-repos), whichever provider it's on. Up to 100 of each per request. +Enables automatic pull and merge request reviews for repositories in `enable` and disables them for repositories in `disable`. Use repository IDs from [List repositories](/api-reference/repos/list-repos) for any supported provider. Each list can contain up to 100 IDs per request. **Scope required**: `write`. **Role**: organization admin. -- A repository must be enabled for Hacktron access before its reviews can be turned on. Enable it from **Repositories** in Hacktron. -- One repository failing doesn't stop the rest. The response counts the repositories that changed (`processed`) and lists each one that didn't with the reason (`failedRepositories`). -- `webhookPending` lists repositories that were turned on but whose provider webhook couldn't be registered yet. Reviews start once it is. +- First, enable Hacktron access to the repository from **Repositories** in Hacktron. +- If one repository fails, Hacktron continues with the others. The response reports how many changed in `processed` and lists failures with their reasons in `failedRepositories`. +- `webhookPending` lists enabled repositories whose provider webhook is still pending registration. Reviews begin once the webhook is registered. diff --git a/api-reference/repos/update-pr-review-settings.mdx b/api-reference/repos/update-pr-review-settings.mdx index 6cb5acb..9aeb933 100644 --- a/api-reference/repos/update-pr-review-settings.mdx +++ b/api-reference/repos/update-pr-review-settings.mdx @@ -4,10 +4,10 @@ openapi: "PATCH /pr-review-settings" description: "Change how PR reviews behave across your organization." --- -Changes your organization's PR review settings and returns all of them. Only the fields you send change. Send `null` to turn off `fail_on_severity` or clear a filter. +Updates your organization's PR review settings and returns the full settings object. Only fields in the request change. Send `null` to disable `fail_on_severity` or clear a filter. **Scope required**: `write`. **Role**: organization admin. -A filter is replaced as a whole. To add one branch, read the settings with [Get PR review settings](/api-reference/repos/get-pr-review-settings) first and send the full list. +Each filter replaces the saved list. To add a branch without removing existing entries, first read [Get PR review settings](/api-reference/repos/get-pr-review-settings), then send the full list with your addition. Branch and author filters take glob patterns (`*`, `**`, `?`, `{a,b}`) and are case-insensitive. Each filter holds up to 50 entries. diff --git a/api-reference/scans/stop-scan.mdx b/api-reference/scans/stop-scan.mdx index db86295..b28c2c9 100644 --- a/api-reference/scans/stop-scan.mdx +++ b/api-reference/scans/stop-scan.mdx @@ -4,6 +4,6 @@ openapi: "POST /scans/{id}/stop" description: "Stop a running scan or pentest." --- -Stops a scan that is running and returns its status. Members can stop pentests they started and any PR scan, and organization admins can stop any scan. +Stops a running scan and returns its status. Members can stop their own pentests and any PR scan. Organization admins can stop any scan. **Scope required**: `write`. **Role**: member or above. diff --git a/code-review/integrations/slack.mdx b/code-review/integrations/slack.mdx index d92d97b..8045011 100644 --- a/code-review/integrations/slack.mdx +++ b/code-review/integrations/slack.mdx @@ -61,7 +61,7 @@ so that Hacktron can recognize you on Slack. -You can now provide feedback and act on findings directly from Slack, and [mention @Hacktron AI](/platform/slack-assistant) to get work done in Hacktron as yourself. +You can now provide feedback and update findings from Slack. To ask about findings, prepare a pentest or manage PR reviews, [mention @Hacktron AI](/platform/slack-assistant). - Create an organization API key — see [Authentication](/api-reference/authentication#creating-an-api-key). Grant `read` & `write` access to let your assistant triage findings. + Create an organization API key using the [Authentication guide](/api-reference/authentication#creating-an-api-key). Grant `read` and `write` access if you want your assistant to triage findings. Run in terminal: @@ -53,7 +53,7 @@ description: "Choose your platform and copy the config. No API key required." When Claude Desktop connects to Hacktron for the first time, complete the Hacktron browser sign-in. If the prompt does not appear, open Claude Desktop's MCP server settings and reconnect `hacktron`. - Create an organization API key — see [Authentication](/api-reference/authentication#creating-an-api-key). Grant `read` & `write` access to let your assistant triage findings. + Create an organization API key using the [Authentication guide](/api-reference/authentication#creating-an-api-key). Grant `read` and `write` access if you want your assistant to triage findings. ```json { @@ -91,7 +91,7 @@ description: "Choose your platform and copy the config. No API key required." When Cursor prompts you to authenticate `hacktron`, complete the Hacktron browser sign-in. You can also open Cursor's MCP settings, select `hacktron`, and start authentication from there. - Create an organization API key — see [Authentication](/api-reference/authentication#creating-an-api-key). Grant `read` & `write` access to let your assistant triage findings. + Create an organization API key using the [Authentication guide](/api-reference/authentication#creating-an-api-key). Grant `read` and `write` access if you want your assistant to triage findings. ```json { @@ -129,7 +129,7 @@ description: "Choose your platform and copy the config. No API key required." Complete the Hacktron browser sign-in. In an interactive Codex session, type `/mcp` to check the server's connection and authentication status. - Create an organization API key — see [Authentication](/api-reference/authentication#creating-an-api-key). Grant `read` & `write` access to let your assistant triage findings. + Create an organization API key using the [Authentication guide](/api-reference/authentication#creating-an-api-key). Grant `read` and `write` access if you want your assistant to triage findings. **1.** Add your API key to your shell profile (`~/.zshrc` or `~/.bashrc`): @@ -172,17 +172,19 @@ description: "Choose your platform and copy the config. No API key required." ## Tools -Each tool calls the same endpoint as the [REST API](/api-reference/introduction), with the same permissions. Viewers can read, members can change findings and set up pentests, and only organization admins can start a pentest. +Use these tools to work with Hacktron from your AI assistant. The linked [REST API endpoints](/api-reference/introduction) provide the same operations and permission requirements. + +Viewers can read scans and findings. Members can update findings and prepare pentests. Starting a pentest or changing PR review settings requires an organization admin. ### Scans and findings | Name | Description | REST endpoint | | --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------ | -| `list_scans` | List scans with severity breakdown | [`GET /scans`](/api-reference/scans/list-scans) | +| `list_scans` | List scans with finding counts by severity | [`GET /scans`](/api-reference/scans/list-scans) | | `get_scan` | Get details about a scan | [`GET /scans/{id}`](/api-reference/scans/get-scan) | | `export_findings` | Export a scan's findings as JSON, CSV, or SARIF | [`GET /scans/{id}/findings/export`](/api-reference/scans/export-scan-findings) | | `stop_scan` | Stop a running scan or pentest | [`POST /scans/{id}/stop`](/api-reference/scans/stop-scan) | -| `list_findings` | List findings across the organisation | [`GET /findings`](/api-reference/findings/list-findings) | +| `list_findings` | List findings across your organization | [`GET /findings`](/api-reference/findings/list-findings) | | `count_findings` | Count findings by severity, state and repository | [`GET /findings/counts`](/api-reference/findings/count-findings) | | `get_finding` | Get details about a finding | [`GET /findings/{id}`](/api-reference/findings/get-finding) | | `update_finding` | Update a finding's state or severity | [`PATCH /findings/{id}`](/api-reference/findings/update-finding) | @@ -190,31 +192,31 @@ Each tool calls the same endpoint as the [REST API](/api-reference/introduction) ### Pentests -| Name | Description | REST endpoint | -| ------------------------ | ---------------------------------------------------- | ----------------------------------------------------------------------------------- | -| `list_repositories` | List connected repositories | [`GET /repos`](/api-reference/repos/list-repos) | -| `request_archive_upload` | Get a URL to upload a code archive | [`POST /archives/uploads`](/api-reference/archives/request-archive-upload) | -| `create_pentest_draft` | Set up a pentest of code, optionally with live sites | [`POST /pentests`](/api-reference/pentests/create-pentest) | -| `update_pentest_draft` | Change a pentest that hasn't started | [`PATCH /pentests/{id}`](/api-reference/pentests/update-pentest) | -| `verify_pentest_login` | Check that Hacktron can sign in to each site | [`POST /pentests/{id}/login-checks`](/api-reference/pentests/check-pentest-logins) | -| `estimate_pentest` | Price a pentest | [`POST /pentests/{id}/estimate`](/api-reference/pentests/estimate-pentest) | -| `get_cost_estimation` | Get a pentest cost estimate | [`GET /cost-estimations/{id}`](/api-reference/cost-estimations/get-cost-estimation) | -| `get_credit_balance` | Get the organisation's pentest credit balance | [`GET /credits`](/api-reference/credits/get-credit-balance) | -| `start_pentest` | Start a pentest and spend its credits (admins only) | [`POST /pentests/{id}/start`](/api-reference/pentests/start-pentest) | -| `discard_pentest_draft` | Delete a pentest that hasn't started | [`DELETE /pentests/{id}`](/api-reference/pentests/discard-pentest) | +| Name | Description | REST endpoint | +| ------------------------ | --------------------------------------------------- | ----------------------------------------------------------------------------------- | +| `list_repositories` | List connected repositories | [`GET /repos`](/api-reference/repos/list-repos) | +| `request_archive_upload` | Get a URL to upload a code archive | [`POST /archives/uploads`](/api-reference/archives/request-archive-upload) | +| `create_pentest_draft` | Create a draft with code and optional live sites | [`POST /pentests`](/api-reference/pentests/create-pentest) | +| `update_pentest_draft` | Change a pentest that hasn't started | [`PATCH /pentests/{id}`](/api-reference/pentests/update-pentest) | +| `verify_pentest_login` | Check that Hacktron can sign in to each site | [`POST /pentests/{id}/login-checks`](/api-reference/pentests/check-pentest-logins) | +| `estimate_pentest` | Estimate a draft pentest's credit cost | [`POST /pentests/{id}/estimate`](/api-reference/pentests/estimate-pentest) | +| `get_cost_estimation` | Get a pentest cost estimate | [`GET /cost-estimations/{id}`](/api-reference/cost-estimations/get-cost-estimation) | +| `get_credit_balance` | Read your organization's pentest credit balance | [`GET /credits`](/api-reference/credits/get-credit-balance) | +| `start_pentest` | Start a pentest and spend its credits (admins only) | [`POST /pentests/{id}/start`](/api-reference/pentests/start-pentest) | +| `discard_pentest_draft` | Delete a pentest that hasn't started | [`DELETE /pentests/{id}`](/api-reference/pentests/discard-pentest) | ### PR reviews -| Name | Description | REST endpoint | -| --------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| `get_pr_review_settings` | Get how PR reviews behave across the organisation | [`GET /pr-review-settings`](/api-reference/repos/get-pr-review-settings) | -| `update_pr_review_settings` | Change PR review comments, fail-on severity and filters (admins only) | [`PATCH /pr-review-settings`](/api-reference/repos/update-pr-review-settings) | -| `set_pr_reviews` | Turn PR reviews on or off for repositories (admins only) | [`POST /repos/pr-reviews`](/api-reference/repos/set-pr-reviews) | +| Name | Description | REST endpoint | +| --------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `get_pr_review_settings` | Read your organization's PR review settings | [`GET /pr-review-settings`](/api-reference/repos/get-pr-review-settings) | +| `update_pr_review_settings` | Update review comments, the check's severity threshold and filters (admins only) | [`PATCH /pr-review-settings`](/api-reference/repos/update-pr-review-settings) | +| `set_pr_reviews` | Turn PR reviews on or off for repositories (admins only) | [`POST /repos/pr-reviews`](/api-reference/repos/set-pr-reviews) | - To test a site signed in, your assistant sends the login details to Hacktron, - where they're stored encrypted. Anything you type in the conversation also - goes to your assistant's model provider, so use a test account. + For signed-in testing, your assistant sends login details to Hacktron for + encrypted storage. Anything you type in the conversation also goes to your + assistant's model provider. Use dedicated test credentials. ## Related @@ -225,9 +227,9 @@ Each tool calls the same endpoint as the [REST API](/api-reference/introduction) icon="key" href="/api-reference/authentication" > - Create and scope the API key used for API-key auth. + Create an API key and choose its access scopes. - Every tool, over the REST API. + Use the same operations from your own scripts and applications. diff --git a/platform/communication-apps/slack.mdx b/platform/communication-apps/slack.mdx index 857f6d7..7f28de5 100644 --- a/platform/communication-apps/slack.mdx +++ b/platform/communication-apps/slack.mdx @@ -28,8 +28,8 @@ You also need permission to authorize apps in the Slack workspace you want Hackt Hacktron uses this access to send security notifications and scan updates - to the Slack channel you configure, and to answer people who mention - @Hacktron AI. + to your configured channel. It also lets people work with Hacktron by + mentioning @Hacktron AI. @@ -64,7 +64,7 @@ Open the Slack card in **Integrations** and choose **Disconnect**. Hacktron will ## Next: use Hacktron in Slack -Connecting Slack installs the Hacktron AI app in your workspace. To let people triage findings, set up pentests and manage PR reviews from Slack, add it to channels with `/invite @Hacktron AI` and have each person link their Slack account. +Connecting Slack installs the Hacktron AI app in your workspace. Add it to a channel with `/invite @Hacktron AI`. Each person then links their Slack and Hacktron accounts to use the assistant with their own permissions. Triage findings, set up pentests and manage PR reviews by mentioning @Hacktron diff --git a/platform/slack-assistant.mdx b/platform/slack-assistant.mdx index ab9e27c..d6a09f8 100644 --- a/platform/slack-assistant.mdx +++ b/platform/slack-assistant.mdx @@ -3,39 +3,41 @@ title: "Hacktron in Slack" description: "Mention @Hacktron AI to triage findings, set up pentests and change PR review settings without leaving Slack." --- -Hacktron AI is an assistant that works in your Slack channels and DMs. Mention **@Hacktron AI** with a request and it does the work in Hacktron as you, with your access, and replies in a thread. Keep talking in that thread without mentioning it again. Teammates can join the thread by mentioning @Hacktron AI, and it acts for each person with their own access. +Mention **@Hacktron AI** in a Slack channel or direct message to work with your findings, set up a pentest or manage PR reviews. The assistant uses your Hacktron account and permissions, and replies in a thread. + +You can continue in that thread without mentioning it again. Teammates can join by mentioning @Hacktron AI. Each person's requests use their own account and permissions. ## What you can ask it to do -| Task | Try | -| ------------------------------------------------- | ------------------------------------------------------------------------------- | -| [Look into findings](#ask-about-findings) | "How many critical findings are open in acme/api?" | -| [Triage a finding](#act-on-findings-and-scans) | "Mark the SSRF finding in acme/api as a false positive, the URL is allowlisted" | -| [Set up a pentest](#set-up-a-pentest) | "Pentest acme/api on main and test staging.acme.com signed in" | -| [Manage PR reviews](#manage-pr-reviews) | "Turn on PR reviews for acme/api and acme/web" | -| [Ask how Hacktron works](#ask-how-hacktron-works) | "How do I stop reviews of PRs into release branches?" | +| Task | Example request | +| ------------------------------------------------- | -------------------------------------------------------------------------------------- | +| [Look into findings](#ask-about-findings) | "How many critical findings are open in acme/api?" | +| [Triage a finding](#act-on-findings-and-scans) | "Mark the SSRF finding in acme/api as a false positive because the URL is allowlisted" | +| [Set up a pentest](#set-up-a-pentest) | "Pentest acme/api on main and test staging.acme.com signed in" | +| [Manage PR reviews](#manage-pr-reviews) | "Turn on PR reviews for acme/api and acme/web" | +| [Ask how Hacktron works](#ask-how-hacktron-works) | "How do I stop reviews of PRs into release branches?" | -Before Hacktron changes a finding, a scan or your PR review settings, it shows you an **Approve** button that says what will change. Pentests start only when an organization admin approves them. +Before changing a finding, stopping a scan or updating PR review settings, Hacktron shows you what will change and asks you to click **Approve**. Starting a pentest requires approval from an organization admin. ## Set up - + In Hacktron, go to **Integrations**, find **Slack** under **Communication Apps** and click **Connect**. Approve the Hacktron AI app for your workspace. This installs the app in Slack. See [Slack](/platform/communication-apps/slack) for the full steps. - If your organization connected Slack before Hacktron could read files, - click **Connect** again so Hacktron can read archives people attach. + If Slack is already connected but Hacktron can't read attached archives, + click **Connect** again to grant file access. - + In Hacktron, open **Account**, scroll to **Connected Accounts** and click - **Connect** next to **Slack**. Choose the same workspace. This is how - Hacktron knows who's asking. See [Link your Slack + **Connect** next to **Slack**. Choose the workspace connected to your + organization so Hacktron can match your Slack and Hacktron accounts. See [Link your Slack identity](/code-review/integrations/slack#link-your-slack-identity). - - Mention **@Hacktron AI** with a question. The first time, Hacktron sends - you a private **Connect Hacktron** message that only you can see. Click - it, sign in to Hacktron and allow access. The thread says it's waiting for - you, then Hacktron carries on with your question. + + Mention **@Hacktron AI** with a request. The first time, Hacktron sends + you a private **Connect Hacktron** message. Click the button, sign in to + Hacktron and allow access. Hacktron waits for you to connect, then + continues with your request in the thread. -Hacktron acts as you, with your role: viewers can ask questions, members can set up pentests, and organization admins can start them and change settings. +Your Hacktron role determines what you can do. Viewers can ask questions, members can prepare pentests, and organization admins can start pentests and change PR review settings. ## Ask about findings @@ -71,21 +73,23 @@ Ask in your own words, for example: - "Show me the high severity findings from the last pentest" - "Which repositories have the most open findings?" -Hacktron answers with counts and posts findings as cards that link to them in Hacktron. +Hacktron can return counts or post finding cards with links to the details in Hacktron. ## Act on findings and scans -Ask Hacktron to change a finding's status, add a comment to a finding, stop a running scan or discard a pentest draft. It shows you an **Approve** button that says what will change, then confirms what it did in the thread. +Ask Hacktron to change a finding's status, add a comment, stop a running scan or discard a pentest draft. Review the proposed change and click **Approve**. Hacktron confirms the result in the thread. Each finding card also has buttons to update its status or fix it with AI. ## Ask how Hacktron works -Ask a question about the product, such as "How do I stop reviews of PRs into release branches?". Hacktron answers from these docs and links the page it used. When the docs don't cover it, it says so. +Ask a product question, such as "How do I stop reviews of PRs into release branches?" Hacktron answers from the documentation and links to the relevant page. If the docs don't cover your question, it tells you. ## Manage PR reviews -Ask Hacktron to turn PR reviews on or off for a repository, or to change how they behave across your organization: whether they comment on pull requests, the severity that fails the PR check, and which branches, labels, authors or title keywords get reviewed. Anyone can ask what the settings are. Only organization admins can change them, and Hacktron shows an **Approve** button before it does. +Ask Hacktron to turn PR reviews on or off for a repository. You can also change organization settings, including whether reviews post comments, which finding severity fails the PR check, and filters for target branches, labels, authors and title keywords. + +Anyone can ask to see the settings. Only organization admins can change them, after reviewing the change and clicking **Approve**. ## Set up a pentest @@ -95,12 +99,12 @@ Ask Hacktron to turn PR reviews on or off for a repository, or to change how the of the code (up to 2 GB) to your message. - To test only part of the code, say so in the thread, for example "only the - services/payments folder". There's no need to upload a smaller archive. + To test part of the code, name the folders in the thread, for example "only + the services/payments folder". You can keep the same archive. - Give Hacktron the URL of a deployed copy of the app to test it live as well - as the code. A staging copy is the safest choice. Without one, the pentest + Provide a deployed app URL if you want live testing alongside the code + review. Use a staging environment where possible. Without a URL, the pentest reviews the code only. @@ -117,29 +121,29 @@ Ask Hacktron to turn PR reviews on or off for a repository, or to change how the ### Test a site signed in -If the live site needs a login, Hacktron posts an **Add login details** button. It opens a private form that only you can see. The details go straight to Hacktron, are stored encrypted, and never appear in the thread or reach the assistant. +If the live site requires a login, click **Add login details** to open a private form. The form sends your details directly to Hacktron for encrypted storage. It keeps them out of the Slack thread and the assistant's conversation. Use this form rather than pasting credentials into Slack. Hacktron then checks that it can sign in and posts the result in the thread. If the check fails, click **Update login details** to fix them. -A pentest tests its live sites either all signed in or all without a login. To test both kinds, set up a separate pentest for each. +A pentest uses the same access mode for all its live sites: signed in or without a login. To test both, create separate pentests. - Signed-in testing is enabled per organization. If it isn't available for - yours, Hacktron says so and can still test the site without signing in. + Signed-in testing must be enabled for your organization. If it isn't + available, Hacktron lets you know. You can still test without signing in. ## Follow what it's doing -While it works, Hacktron shows its plan and each step it takes in a progress card at the top of its reply. +A progress card at the top of the reply shows Hacktron's plan and the steps it takes. ## Limits -- Hacktron reads attached archives to understand the code, but never runs your code. To test the running app, give it a deployed site. +- The Slack assistant can read attached archives, but it doesn't build or run your code. Provide a deployed app URL for live testing. - Each thread has a usage limit. When a thread reaches it, Hacktron stops and asks you to mention it in a new thread. -- If Hacktron can't read files you attach, ask an organization admin to reconnect Slack from **Integrations** so the app gets permission to read files. +- If Hacktron can't read an attachment, ask an organization admin to reconnect Slack from **Integrations** to grant file access. ## Troubleshooting - **Hacktron doesn't reply to a mention in a channel.** Add it to the channel with `/invite @Hacktron AI`, and check that Slack is connected under **Integrations** in Hacktron. - **"Hacktron couldn't find your account for this workspace."** Link your Slack account from **Account** > **Connected Accounts** in Hacktron, choosing the same workspace, then try again. -- **A reply without a mention gets no answer.** Only the person who started the thread can reply without mentioning Hacktron. Everyone else mentions **@Hacktron AI** in the thread. +- **Hacktron doesn't answer a reply in a thread.** If someone else started the thread, mention **@Hacktron AI** in your reply. Only the person who started it can continue without a mention. diff --git a/white-box-pentest/quickstart.mdx b/white-box-pentest/quickstart.mdx index b6e9a49..3690b04 100644 --- a/white-box-pentest/quickstart.mdx +++ b/white-box-pentest/quickstart.mdx @@ -199,14 +199,14 @@ For a public target: ## Other ways to start a pentest -You can also set up and start a pentest from Slack, from an AI assistant -connected to the Hacktron MCP server, or from the REST API. Each follows the -same steps as the dashboard: set the scope, estimate the credits, then start. -Only organization admins can start a pentest. +You can also prepare a pentest in Slack, through an AI assistant connected to +Hacktron's MCP server, or through the REST API. Set the scope and review the +credit estimate before starting. These options require an organization admin +to start the pentest. - Mention @Hacktron with a repository or an attached archive. + Mention @Hacktron AI with a repository or an attached archive. Set up pentests from Claude, Cursor, and other AI assistants. @@ -216,7 +216,7 @@ Only organization admins can start a pentest. icon="code" href="/api-reference/pentests/create-pentest" > - Set up a pentest of code and live sites over the REST API. + Create a draft with repositories or archives, and optional live sites. Date: Sat, 31 Oct 2026 20:51:50 +0000 Subject: [PATCH 09/10] docs: clean up Slack example prompt formatting --- platform/slack-assistant.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/platform/slack-assistant.mdx b/platform/slack-assistant.mdx index d6a09f8..1201a77 100644 --- a/platform/slack-assistant.mdx +++ b/platform/slack-assistant.mdx @@ -9,13 +9,13 @@ You can continue in that thread without mentioning it again. Teammates can join ## What you can ask it to do -| Task | Example request | -| ------------------------------------------------- | -------------------------------------------------------------------------------------- | -| [Look into findings](#ask-about-findings) | "How many critical findings are open in acme/api?" | -| [Triage a finding](#act-on-findings-and-scans) | "Mark the SSRF finding in acme/api as a false positive because the URL is allowlisted" | -| [Set up a pentest](#set-up-a-pentest) | "Pentest acme/api on main and test staging.acme.com signed in" | -| [Manage PR reviews](#manage-pr-reviews) | "Turn on PR reviews for acme/api and acme/web" | -| [Ask how Hacktron works](#ask-how-hacktron-works) | "How do I stop reviews of PRs into release branches?" | +| Task | Example request | +| ------------------------------------------------- | ------------------------------------------------------------------------------------ | +| [Look into findings](#ask-about-findings) | How many critical findings are open in acme/api? | +| [Triage a finding](#act-on-findings-and-scans) | Mark the SSRF finding in acme/api as a false positive because the URL is allowlisted | +| [Set up a pentest](#set-up-a-pentest) | Pentest acme/api on main and test staging.acme.com signed in | +| [Manage PR reviews](#manage-pr-reviews) | Turn on PR reviews for acme/api and acme/web | +| [Ask how Hacktron works](#ask-how-hacktron-works) | How do I stop reviews of PRs into release branches? | Before changing a finding, stopping a scan or updating PR review settings, Hacktron shows you what will change and asks you to click **Approve**. Starting a pentest requires approval from an organization admin. From bd49e0b010eee0af919b0350f6006a7ffd441ee3 Mon Sep 17 00:00:00 2001 From: "Zeyu (Zayne) Zhang" Date: Sat, 31 Oct 2026 20:58:36 +0000 Subject: [PATCH 10/10] docs: align Slack setup with current UI and add product screenshots --- api-reference/pentests/discard-pentest.mdx | 2 +- api-reference/pentests/update-pentest.mdx | 2 +- code-review/integrations/slack.mdx | 16 ++++++------- images/slack-connected-accounts.png | Bin 0 -> 17538 bytes images/slack-integration-connect.png | Bin 0 -> 10040 bytes platform/communication-apps/slack.mdx | 21 +++++++++-------- platform/slack-assistant.mdx | 25 +++++++++++++-------- 7 files changed, 35 insertions(+), 31 deletions(-) create mode 100644 images/slack-connected-accounts.png create mode 100644 images/slack-integration-connect.png diff --git a/api-reference/pentests/discard-pentest.mdx b/api-reference/pentests/discard-pentest.mdx index 7d8c54b..ff3dd85 100644 --- a/api-reference/pentests/discard-pentest.mdx +++ b/api-reference/pentests/discard-pentest.mdx @@ -4,6 +4,6 @@ openapi: "DELETE /pentests/{id}" description: "Delete a pentest that hasn't started." --- -Deletes a draft pentest and its saved login details. A pentest that has started can't be discarded; use [Stop scan](/api-reference/scans/stop-scan) instead. +Deletes a draft pentest and its saved login details. To end a pentest that has already started, use [Stop scan](/api-reference/scans/stop-scan). **Scope required**: `write`. **Role**: member or above. diff --git a/api-reference/pentests/update-pentest.mdx b/api-reference/pentests/update-pentest.mdx index db00939..5921350 100644 --- a/api-reference/pentests/update-pentest.mdx +++ b/api-reference/pentests/update-pentest.mdx @@ -4,7 +4,7 @@ openapi: "PATCH /pentests/{id}" description: "Change a pentest that hasn't started." --- -Updates a draft pentest. Fields in the request replace their saved values; omitted fields keep their current values. You can't change a pentest after it starts. +Updates a draft pentest. Fields in the request replace their saved values. Omitted fields keep their current values. You can't change a pentest after it starts. **Scope required**: `write`. **Role**: member or above. diff --git a/code-review/integrations/slack.mdx b/code-review/integrations/slack.mdx index 8045011..0617666 100644 --- a/code-review/integrations/slack.mdx +++ b/code-review/integrations/slack.mdx @@ -27,20 +27,18 @@ Open the finding card and click on **Mark Valid** or **Triage Comment**. ## Link your Slack identity -To perform actions on findings directly from Slack and for Hacktron to tag -you on Slack, you need to link your Slack identity to your Hacktron account -so that Hacktron can recognize you on Slack. +Link your Slack and Hacktron accounts so you can update findings from Slack +and Hacktron can mention you in notifications. - - Click on your organization name in the top left corner and go to - **Account**. + + Open **Personal settings** in Hacktron. - Scroll down to **Connected Accounts** and click on **Connect** next to + Find **Connected accounts** and click **Connect** next to **Slack**. - Connected Accounts + Connected accounts in Personal settings, with a Connect button next to Slack @@ -57,7 +55,7 @@ so that Hacktron can recognize you on Slack. - After authorization, Hacktron redirects you back to **Account**. + After authorization, Hacktron redirects you back to **Personal settings**. diff --git a/images/slack-connected-accounts.png b/images/slack-connected-accounts.png new file mode 100644 index 0000000000000000000000000000000000000000..4f702494d0f20901f63fd4e8fc972525ad1d7056 GIT binary patch literal 17538 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 - + In Hacktron, go to **Integrations** and find **Slack** under - **Communication Apps**. + **Notifications**. - Slack integration card in Hacktron + Slack under Notifications in Integrations, with a Connect button - - Click **Connect**. Your browser will redirect to Slack. + + Click **Connect**, then **Connect Slack** in the panel that opens. + Your browser redirects to Slack. Sign in to Slack if required, choose the workspace, and approve the @@ -37,28 +38,26 @@ You also need permission to authorize apps in the Slack workspace you want Hackt After authorization, Hacktron redirects you back to **Integrations**. - Click the settings icon on the Slack card. + Click **Manage** next to Slack to open its panel. Select the Slack channel that should receive Hacktron scan updates. The setting saves when you choose a different channel. - Slack channel configuration - ## Updating the channel -Open the Slack settings panel again and choose a different channel. Hacktron removes the previous channel configuration and saves the new one. +Open the Slack panel again and choose a different channel. Hacktron saves the new destination for notifications. ## Disconnecting Slack -Open the Slack card in **Integrations** and choose **Disconnect**. Hacktron will stop sending notifications and alerts to Slack. +In **Integrations**, click **Manage** next to Slack, then **Disconnect Slack**. Confirm the disconnection to stop notifications and alerts. ## Common checks -- If Slack does not connect, retry from **Integrations** so Hacktron can create a fresh OAuth state. +- If Slack does not connect, retry from **Integrations**. - If a channel is missing, confirm that the Slack workspace account used for OAuth can access that channel. - If notifications are not appearing, confirm that the Slack integration is connected and a channel is selected. diff --git a/platform/slack-assistant.mdx b/platform/slack-assistant.mdx index 1201a77..71004c6 100644 --- a/platform/slack-assistant.mdx +++ b/platform/slack-assistant.mdx @@ -23,26 +23,33 @@ Before changing a finding, stopping a scan or updating PR review settings, Hackt - In Hacktron, go to **Integrations**, find **Slack** under **Communication - Apps** and click **Connect**. Approve the Hacktron AI app for your - workspace. This installs the app in Slack. See + In Hacktron, go to **Integrations** and find **Slack** under + **Notifications**. Click **Connect**, then **Connect Slack** in the panel + that opens. Approve the Hacktron AI app for your workspace to install it. + See [Slack](/platform/communication-apps/slack) for the full steps. + Slack under Notifications in Integrations, with a Connect button + If Slack is already connected but Hacktron can't read attached archives, - click **Connect** again to grant file access. + disconnect Slack and connect it again from **Integrations** to grant + file access. - In Hacktron, open **Account**, scroll to **Connected Accounts** and click + In Hacktron, open **Personal settings**, find **Connected accounts** and click **Connect** next to **Slack**. Choose the workspace connected to your organization so Hacktron can match your Slack and Hacktron accounts. See [Link your Slack identity](/code-review/integrations/slack#link-your-slack-identity). Slack under Connected Accounts in Hacktron @@ -140,10 +147,10 @@ A progress card at the top of the reply shows Hacktron's plan and the steps it t - The Slack assistant can read attached archives, but it doesn't build or run your code. Provide a deployed app URL for live testing. - Each thread has a usage limit. When a thread reaches it, Hacktron stops and asks you to mention it in a new thread. -- If Hacktron can't read an attachment, ask an organization admin to reconnect Slack from **Integrations** to grant file access. +- If Hacktron can't read an attachment, ask an organization admin to disconnect Slack and connect it again from **Integrations** to grant file access. ## Troubleshooting - **Hacktron doesn't reply to a mention in a channel.** Add it to the channel with `/invite @Hacktron AI`, and check that Slack is connected under **Integrations** in Hacktron. -- **"Hacktron couldn't find your account for this workspace."** Link your Slack account from **Account** > **Connected Accounts** in Hacktron, choosing the same workspace, then try again. +- **"Hacktron couldn't find your account for this workspace."** Link your Slack account from **Personal settings** > **Connected accounts** in Hacktron, choosing the same workspace, then try again. - **Hacktron doesn't answer a reply in a thread.** If someone else started the thread, mention **@Hacktron AI** in your reply. Only the person who started it can continue without a mention.