Skip to content

Security

The security resource exposes two halves of the platform’s security pipeline as API: vulnerabilities — known CVEs matched against each site’s installed plugins, themes, and core — and malware — scans and detections from the ClamAV engine, available per site and per environment. An account-wide rollup summarizes both across everything you own. Together they let you drive the full detect → quarantine → verify → restore loop from code, with an audit trail, the way a security MSSP would.

For what the pipeline is and how each layer works, see security overview and malware scanning. This page covers reading and acting on that data over the API.

Action Scope
Read malware overviews, scans, detections security:read
Trigger a scan, restore or dismiss a detection security:write
Read vulnerability findings vulnerabilities:read

security:write implies security:read, but vulnerabilities:read is its own resource — security:read does not imply it. A monitoring key that should see both halves needs both scopes, and the account rollup below requires both. As everywhere, the down-scoping rule intersects a key’s scopes against its owner’s role — a key can never see more than the human who minted it.

GET /v1/sites/{siteID}/security/vulnerabilities  

vulnerabilities:read

Open vulnerability findings matched against the site’s software inventory, sourced from the Wordfence Intelligence vulnerability feed. The response has three parts: a summary of open counts by severity, the findings themselves, and the attribution notices.

Response (trimmed)
{
"data": {
"summary": {
"critical": 1, "high": 0, "medium": 2, "low": 0,
"total": 3,
"checked_at": "2026-07-04T06:00:12Z",
"inventory_status": "available"
},
"findings": [
{
"vuln_id": "8c4b…",
"title": "Example Forms <= 3.1.4 - Unauthenticated SQL Injection",
"component_type": "plugin",
"component_name": "Example Forms",
"component_slug": "example-forms",
"installed_version": "3.1.2",
"affected_range": "<= 3.1.4",
"fixed_version": "3.1.5",
"patched": false,
"cvss_score": 9.8,
"cvss_rating": "Critical",
"cve": "CVE-2026-12345",
"remediation": "Update to version 3.1.5 or newer.",
"record_url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/8c4b…"
}
],
"attribution": {
"defiant_notice": "Vulnerability data © Defiant Inc. …",
"defiant_license_url": "https://www.wordfence.com/…",
"mitre_notice": "CVE data © MITRE Corporation …",
"mitre_license_url": "https://cve.mitre.org/…"
}
},
"request_id": "req_01JAB1"
}
List a site's open vulnerabilities
curl https://api.managed.dev/v1/sites/site_01J7Q2/security/vulnerabilities \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23"

Malware scanning works at both levels: site paths under /v1/sites/{siteID}/malware… cover the production site, and every endpoint has an environment twin under /v1/sites/{siteID}/environments/{envID}/malware… for scanning a staging or preview environment in isolation.

Method + path (…/malware) Scope Returns / does
GET …/malware security:read current overview — engine status, last scan, active detections
GET …/malware/scans security:read scan history, newest first
POST …/malware/scan security:write start an on-demand scan → 202 + a job
POST …/malware/detections/{detID}/restore security:write restore a quarantined file (optional mark_ignored)
POST …/malware/detections/{detID}/dismiss security:write dismiss a detection without restoring the file

Each detection row carries the file path, the matched signature, a sha256, and a statusdetected, quarantined, quarantine_failed, restored, ignored, or dismissed. Infected files are quarantined automatically; restore and dismiss are how you resolve what the scanner caught.

GET /v1/security/summary  

security:readvulnerabilities:read

One call, your whole security posture: for every site you own, open vulnerability counts by severity (critical/high/medium/low plus vuln_total), the number of active malware detections (malware_active), and the last scan’s timestamp and outcome. It requires both scopes because it joins both halves. A resource-pinned key gets a rollup scoped to its pin, and truncated is true when your site set exceeds the per-request cap.

Account-wide security posture
mf security summary

The endpoints compose into one loop you can run entirely from an integration — this is what closes the gap a bare “site hacked” alert only opens:

  1. Detect. A malware.detected or vulnerability.matched webhook arrives (both are critical severity), or a scheduled scan surfaces a detection. Read the overview with GET …/malware.
  2. Inspect. Each detection carries the flagged file’s path, the matched signature, its sha256, and whether quarantine succeeded. The file is already quarantined — the site is safe while you decide.
  3. Remediate. Either POST …/detections/{detID}/restore — with {"mark_ignored": true} if it’s a false positive, so later scans don’t re-quarantine the same hash — or POST …/detections/{detID}/dismiss to accept the quarantine and move on.
  4. Verify. Kick POST …/malware/scan and wait on the job until it reports clean.
  5. Audit. Every write lands in the audit log, so the whole remediation is reviewable after the fact.

POST to start an on-demand scan. Pass an Idempotency-Key so a retry never queues a second scan.

Start a malware scan
curl -X POST https://api.managed.dev/v1/sites/site_01J7Q2/malware/scan \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23" \
-H "Idempotency-Key: scan-2026-07-04-checkout"
Response — 202 Accepted (Location: /v1/jobs/job_01J9SC)
{
"data": {
"id": "job_01J9SC",
"type": "malware.scan",
"status": "queued",
"progress": 0,
"created_at": "2026-07-04T01:02:00.110Z",
"resource": { "type": "malware", "id": "site_01J7Q2", "site_id": "site_01J7Q2" },
"links": { "self": "/v1/jobs/job_01J9SC", "stream": "/v1/jobs/job_01J9SC/stream" }
},
"request_id": "req_01JAB2"
}

Worked example — review and restore a detection

Section titled “Worked example — review and restore a detection”

When a scan flags a file that’s actually yours, restore it and stop future scans from re-quarantining it.

Inspect, then restore a detection
# read the overview — detections ride along
curl https://api.managed.dev/v1/sites/site_01J7Q2/malware \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23"
# restore the file and ignore its hash going forward
curl -X POST https://api.managed.dev/v1/sites/site_01J7Q2/malware/detections/det_01JK4/restore \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23" \
-H "Idempotency-Key: restore-det_01JK4" \
-H "Content-Type: application/json" \
-d '{ "mark_ignored": true }'
Response — the restored detection
{
"data": {
"id": "det_01JK4",
"site_id": "site_01J7Q2",
"source": "scan",
"path": "wp-content/uploads/2026/06/custom-tool.php",
"signature": "PHP.Backdoor.Generic-9831",
"sha256": "9f2c…",
"status": "restored",
"detected_at": "2026-07-04T00:58:11Z",
"updated_at": "2026-07-04T01:12:40Z"
},
"request_id": "req_01JAB7"
}

The Security → Blocks view — every request the WAF, rate limiter, Patchstack, or login lockout rejected — is a dashboard feature today. Block-list and block-volume endpoints are designed but not yet in the API; this section will document them when they ship.