0 · Is Aegis right for you?

The most useful thing we can tell you is when the answer is no.

✓ Aegis is a good fit if…

  • You have a web application or API and need it tested before go-live.
  • A customer, investor or auditor is asking for a penetration test report (SOC 2, ISO 27001, a security questionnaire).
  • You want authenticated testing — behind the login, where the real risk is — not just a surface scan.
  • You run multi-tenant SaaS and need tenant isolation proven.
  • You want results today, repeatably, without scheduling a consultancy.
  • A $4,000–$15,000 engagement is out of proportion to your stage.

✗ Aegis is the wrong tool if…

  • You need a human red team — creative chained exploits, novel zero-days, physical or social engineering. Hire a consultancy; we are not a substitute.
  • You need a signed attestation from a certified assessor (some PCI DSS and government procurement paths require a qualified, independent human tester).
  • Your priority is source-code and dependency scanning (SAST/SCA/containers). Aikido, Snyk and Semgrep do that well; we test the running application instead — many customers use both.
  • You need network, infrastructure or cloud-configuration testing. We test applications over HTTP(S).
  • You cannot prove domain ownership. No proof, no test — no exceptions.
  • You want someone to fix the findings for you. We tell you precisely what and how; the change is yours to make.
How we compare, honestly.
What you needBest choiceWhy
Audit-ready pentest report, fast & affordableAegis — $1,499 USDSame automation category as a $4,000 AI pentest; a fraction of the price.
Broad code/dependency/container/cloud coverageAikido, SnykGenuinely broader platforms. We don't do SAST or SCA.
Continuous surface monitoring of many assetsIntruder, DetectifyBuilt for breadth of assets rather than depth per application.
Human-led red team / novel exploitationA consultancyCreativity is not automatable. Expect $10k+.
Business-logic & multi-tenant authorization depthAegisTwo-identity differential testing most scanners don't attempt.

1 · How testing works, end to end

Five stages. Nothing runs until ownership is proven.

StageWhat happens
1. Ownership proofYou add a DNS TXT record. We resolve it against public DNS. No verification, no scan — there is no manual override.
2. Rules of engagementYou confirm scope, rate limits, excluded paths, backup readiness and an emergency contact. Recorded with a timestamp as your authorisation.
3. DiscoveryWe map the reachable surface — pages, endpoints, forms, parameters. For authenticated tiers we sign in first, so we see what real users see.
4. TestingChecks run inside enforced budgets: capped requests, limited concurrency, throttled rate. Every request is logged.
5. ReportFindings with evidence, business impact and remediation — plus the controls we attacked and confirmed holding.

2 · What each severity actually means

Severity is about consequence, not how alarming the name sounds.

SeverityMeaningAct within
CriticalDirectly exploitable now, with serious consequence — full account takeover, mass data exposure, remote code execution.Immediately. Before your next release.
HighExploitable with modest effort, or exposes sensitive data to the wrong party.Days.
MediumNot exploitable alone, but removes a defensive layer or meaningfully helps an attacker.This sprint.
LowDefence-in-depth gap. Little standalone risk; compounds with others.Backlog.
InfoNo risk today. Worth knowing — often hygiene or drift.When convenient.

Severities interact. A reflected parameter (Low) plus a weak Content-Security-Policy (Medium) is materially worse than either alone: the CSP is what would have contained the reflection if it ever became injectable. We call out these combinations in the report.

3 · Transport & TLS

HTTP Strict-Transport-Security (HSTS) Low All tiers

How we test it

We request your site over HTTPS and inspect the response for a Strict-Transport-Security header, checking max-age and whether subdomains are covered.

What the result means

Missing: a visitor's first request — or one where an attacker strips TLS on a hostile network — can travel over plain HTTP, exposing the session cookie. Present: browsers refuse HTTP for your domain entirely after the first visit.

How to fix
  1. Add the header at your edge or origin:
    Strict-Transport-Security: max-age=31536000; includeSubDomains
  2. Confirm every subdomain is HTTPS-ready before adding includeSubDomains — it applies to all of them.
  3. Once stable, consider submitting to the browser preload list.

4 · Security headers

X-Content-Type-Options, Referrer-Policy, Permissions-Policy Low All tiers

How we test it

We read response headers across several pages — not just the homepage, since headers are often applied inconsistently by route.

What the result means

No nosniff: a browser may guess a file's type and execute an upload as script. No Referrer-Policy: full URLs (which may carry tokens or IDs) leak to third-party sites. No Permissions-Policy: embedded content can request camera, microphone or location.

How to fix
  1. Set all three globally at the edge so no route can miss them:
    X-Content-Type-Options: nosniff
    Referrer-Policy: strict-origin-when-cross-origin
    Permissions-Policy: camera=(), microphone=(), geolocation=()
  2. Re-scan to confirm they appear on API responses and error pages too.

5 · Cookies & sessions

Cookie security attributes Medium All tiers

How we test it

We inspect every Set-Cookie for Secure, HttpOnly and SameSite, and identify which cookie carries the session.

What the result means

No Secure: the cookie can be sent over plain HTTP and captured. No HttpOnly: any JavaScript — including injected script — can read your session token. No SameSite: the browser attaches the cookie to cross-site requests, enabling CSRF.

How to fix
  1. Set all three on the session cookie: Secure; HttpOnly; SameSite=Lax (use Strict if you have no cross-site flows).
  2. If your framework has an HTTPS flag (e.g. an APP_HTTPS=1 style setting), ensure it is on in production — cookies are often only marked Secure when it is.
  3. Rotate the session-signing secret if it has ever been shared across installs.

6 · Content-Security-Policy & CORS

Content-Security-Policy strength Medium All tiers

How we test it

We parse your CSP and evaluate the script-related directives, flagging unsafe-inline, unsafe-eval, overly broad wildcards and a missing frame-ancestors.

What the result means

CSP is the seatbelt for cross-site scripting. A missing or permissive policy doesn't create a vulnerability — it removes the control that would have contained one. With unsafe-inline, an injected <script> executes exactly as the attacker intended.

How to fix
  1. Start in report-only mode to find breakage without downtime: Content-Security-Policy-Report-Only.
  2. Remove unsafe-inline by moving inline scripts to files or adding a per-request nonce.
  3. Remove unsafe-eval — usually a library doing string-to-code; most have a CSP-safe build.
  4. Add frame-ancestors 'none' (or your allowed embedders) to stop clickjacking.
  5. Enforce, then re-scan. Confirm your CDN doesn't strip or replace the header.

CORS configuration High if permissive All tiers

How we test it

We send cross-origin and preflight requests with varied Origin values to see which are accepted, and whether credentials are permitted.

What the result means

The dangerous combination is reflecting any origin and allowing credentials — that lets any website read authenticated responses on behalf of a logged-in visitor. Access-Control-Allow-Origin: * without credentials is usually fine for public data.

How to fix
  1. Replace origin reflection with a strict allow-list of known origins.
  2. Never combine a wildcard (or reflected) origin with Access-Control-Allow-Credentials: true.
  3. Restrict allowed methods and headers to what the client genuinely uses.

7 · Attack-surface discovery

Endpoint inventory & API spec drift Info All tiers

How we test it

We crawl the reachable application — authenticated, on paid tiers — inventorying URLs, methods, parameters and forms, then compare against your OpenAPI specification if you provide one.

What the result means

Endpoints live in production that aren't in your spec. Undocumented routes escape design review and access-control decisions — historically a common source of forgotten admin and debug endpoints.

How to fix
  1. Review each undocumented route: is it intentional?
  2. Remove debug, test and legacy endpoints from production.
  3. Document the rest, including their authentication requirements.

8 · Injection — SQLi, XSS, SSTI, command, path, SSRF, XXE & open redirect

SQL injection (boolean-based) Critical if found Go-Live

How we test it

We submit pairs of logically true and false conditions to each parameter and compare responses. A page that changes between always-true and always-false is evaluating input as SQL. We use inert boolean logic — never data-modifying or destructive payloads.

What the result means

An attacker can read, and often modify, your database directly — every customer record, password hash and payment detail. This is as serious as application security gets.

How to fix
  1. Use parameterised queries (prepared statements) everywhere. Never build SQL by string concatenation.
  2. In an ORM, avoid raw-SQL escape hatches with interpolated values.
  3. Validate and type-check input, but treat that as secondary — parameterisation is the actual fix.
  4. Give the application's database user the least privilege it needs.
  5. Assume compromise: review logs, and rotate credentials if exploitation is plausible.

Cross-site scripting (inert canary) High if found Go-Live

How we test it

We submit a harmless unique marker and check whether it is reflected into the response, and in what context — HTML body, attribute, or JavaScript. We never inject working exploit code.

What the result means

Reflected and unescaped in an executable context: an attacker can run JavaScript as your users — stealing sessions, altering pages, exfiltrating data. Reflected but correctly escaped: not a vulnerability, but a place to keep encoding correct as the code changes.

How to fix
  1. Escape output for its context — HTML, attribute, JavaScript and URL escaping are different.
  2. Prefer frameworks that escape by default; audit every deliberate bypass (dangerouslySetInnerHTML, v-html, |safe).
  3. Never place untrusted values inside a <script> block.
  4. Tighten CSP (§6) so a future mistake is contained.

SQL injection (error-based) High if found Go-Live

How we test it

We append a single syntax-breaking character to a parameter and watch for a database error that was absent from the baseline response. Read-only — we never extract or modify data.

What the result means

User input is reaching a SQL statement unparameterised. Even without extracting data, a returned database error confirms the query can be broken — a direct path to full SQL injection.

How to fix
  1. Use parameterised queries/prepared statements exclusively.
  2. Disable verbose database errors in production as defence in depth.

SQL injection (time-based, blind) Critical if found Go-Live · opt-in

How we test it

When a parameter returns no error and no visible change, we test for blind injection: we submit input asking the database to pause briefly only if a condition holds, and compare the response time to the baseline. A reliable, condition-dependent delay means the input is executed as SQL. Delays are minimal and bounded — never destructive payloads.

What the result means

The database is injectable even though nothing is reflected back — the kind that error- and content-based checks miss entirely. An attacker can extract data one inference at a time.

How to fix
  1. Parameterise every query — the same fix as all SQL injection.
  2. Add query timeouts and rate limits so a timing oracle is slow and noisy to exploit.

OS command injection Critical if found Go-Live

How we test it

We inject a bounded shell-arithmetic echo (e.g. $((1009×1013))) and check whether the server evaluated it — the computed marker appears only if a shell ran the input. We send nothing destructive: only harmless echo and arithmetic.

What the result means

The application passes user input to an operating-system shell. This is typically full server compromise — an attacker can run arbitrary commands.

How to fix
  1. Never pass user input to a shell. Use parameterised process APIs (argument lists, never shell=True).
  2. Validate against strict allow-lists and remove shell interpolation entirely.

Path traversal High if found Go-Live

How we test it

We inject read-only traversal sequences (../../etc/passwd and encoded variants) and check the response for well-known system-file signatures. Nothing is written or modified.

What the result means

The application turns user input into filesystem paths without containment — an attacker can read files outside the intended directory (configuration, secrets, source).

How to fix
  1. Canonicalise paths and confirm they stay within an allow-listed base directory; reject .. and encoded forms.
  2. Prefer opaque IDs mapped to paths server-side.

Server-side template injection (SSTI) Critical if found Go-Live

How we test it

We inject a bounded arithmetic marker for the common template engines ({{1009*1013}}, ${…}, <%= … %>) and flag it only when the server evaluates the expression — the product appears while the literal does not. No code-execution payloads are sent.

What the result means

User input is rendered as template source. SSTI frequently escalates to remote code execution.

How to fix
  1. Never render user input as a template. Pass untrusted values only as sandboxed data variables.
  2. Patch/upgrade the template engine and enable its sandbox.

Server-side request forgery (SSRF) — reflected & blind Critical if found Go-Live

How we test it

For URL-like parameters we substitute a cloud-metadata address and check whether the metadata response is reflected back (reflected SSRF). We also substitute a unique Aegis out-of-band callback URL and detect whether the server makes a request to it (blind SSRF — where nothing is reflected). The callback records only that the token was reached.

What the result means

The server fetches attacker-controlled URLs. This commonly exposes cloud credentials and internal services that are unreachable from the outside.

How to fix
  1. Do not fetch user-supplied URLs. If unavoidable, enforce a destination allow-list, resolve and pin the IP, and block link-local/loopback/private/metadata ranges.
  2. Disable redirects on server-side fetches.

XML external entity injection (XXE) Critical if found Go-Live · opt-in

How we test it

For endpoints that accept XML, we submit a document defining an external entity pointing at a unique Aegis out-of-band callback URL, then watch whether your parser resolves it and calls us. The callback records only that the token was reached — we never read local files or exfiltrate data. Because this sends a POST body it is an opt-in heavier probe you approve explicitly.

What the result means

Your XML parser resolves external entities. That can read local files (including secrets), reach internal services (a form of SSRF), and in some parsers escalate further.

How to fix
  1. Disable external-entity and DTD processing in your XML parser — the single most important setting.
  2. Prefer JSON where you control the format; validate XML against a strict schema.
  3. Keep the parser patched.

Open redirect Medium if found Pro & above

How we test it

For parameters that control a destination URL, we substitute a benign external marker and check whether the application issues a redirect to it. Read-only — we observe the redirect target, we don't follow it anywhere harmful.

What the result means

An attacker can craft a link on your own domain that silently forwards users to a site they control — a strong aid to phishing, and sometimes a token-theft vector when chained with OAuth or SSO flows.

How to fix
  1. Don't put raw URLs in redirect parameters. Use server-side keys that map to an allow-list of destinations.
  2. If you must accept a URL, allow only same-origin paths and reject absolute or protocol-relative targets.

9 · Authorization & access control

Authorization-differential testing Critical if found Pro & above

How we test it

We sign in as two identities — typically a low-privilege user and a higher-privilege one — then replay identical requests as each and compare. If the low-privilege session receives data it shouldn't, that's broken access control. We check the final response, not just the status code, because many apps return HTTP 200 while redirecting to a "no access" page.

What the result means

Broken access control is the most common serious flaw in modern applications, and automated scanners rarely find it because it requires understanding who should see what. A hit here means one customer can reach another's data, or an ordinary user can perform privileged actions.

How to fix
  1. Enforce authorization server-side on every request. Hiding a button is not access control.
  2. Check ownership, not just authentication: "is this record owned by the caller?" — not merely "is the caller logged in?"
  3. Deny by default; require an explicit grant for each resource.
  4. Centralise the check so new endpoints inherit it instead of re-implementing it.
  5. Add a regression test per role — this class of bug returns easily.

Insecure direct object references (IDOR) High if found Pro & above

How we test it

Where a resource is addressed by an identifier (/invoices/1024), we request neighbouring identifiers as a user who should not have access.

What the result means

Records can be enumerated by changing a number in the URL — a trivial attack requiring no tooling.

How to fix
  1. Verify ownership on every fetch — scope the query to the caller, e.g. WHERE id = ? AND owner_id = ?.
  2. Return 404, not 403, for records the caller may not see — 403 confirms the record exists.
  3. Unguessable identifiers (UUIDs) raise the bar but are not a substitute for the ownership check.

10 · Business-logic & state-changing tests

These change application state, so they are not part of the automated non-destructive Go-Live scan. They run self-serve — only against a non-production target you designate and explicitly approve in-app. No engineers are involved; the platform executes and gates them automatically.

Privilege escalation in workflows Critical if found Aggressive

How we test it

We attempt real state-changing actions as the wrong role — approving one's own request, triggering a payment without finance rights, acting on another party's order. Each attempt is bounded and logged, with a cleanup contract.

What the result means

These are the flaws that cost money rather than data: self-approved purchases, unauthorised payments, tampered orders. Scanners essentially never find them, because they require understanding your workflow — this is the closest automation gets to a human tester.

How to fix
  1. Enforce separation of duties in the backend: the requester must not be the approver; whoever enters bank details must not release payment.
  2. Validate the state transition, not just the role — can this record legally move from here to there?
  3. Log every privileged action with the actor, and make blocked attempts auditable.
  4. Add a regression test per rule.

Cross-site request forgery (CSRF) High if found Aggressive

How we test it

We submit state-changing requests without a valid token, and with a token belonging to a different session, to see whether they are accepted.

What the result means

Another website can make your logged-in users perform actions without their knowledge — changing an email address, transferring funds, approving a request.

How to fix
  1. Require a per-session CSRF token on every state-changing request; reject when missing or mismatched.
  2. Set SameSite=Lax (or Strict) on session cookies as a second layer.
  3. Never make GET requests state-changing.

11 · Multi-tenant isolation

Cross-tenant access & session replay Critical if found Business

How we test it

With two tenants provisioned, we take an authenticated session from tenant A and replay it against tenant B — including by manipulating the host, subdomain or tenant identifier — and separately attempt to fetch tenant B's records by identifier. This needs test logins for both tenants, so it takes a few setup steps: you supply those logins and approve the run in-app. It is fully self-serve — just multi-step rather than one-click. (The underlying dual-identity engine that powers it is live self-serve for same-app IDOR — see §9.)

What the result means

For a SaaS business this is the existential control. A failure means one customer can read another's data — typically a breach-notification event and, often, a company-ending one.

How to fix
  1. Bind the session to its tenant server-side and re-validate on every request. Never infer tenancy from a header, subdomain or cookie alone — those are attacker-controlled.
  2. Scope every query by tenant at the data layer, so a missed check in a controller can't leak.
  3. Prefer per-tenant schemas or databases where practical — isolation by construction beats isolation by discipline.
  4. Test it continuously. This control silently degrades as features are added.

12 · Verified controls — what we prove is safe

The section other reports don't have.

Most reports list only what's broken. That leaves an auditor asking the obvious question: "what did you actually try?"

Every Aegis report includes a Verified Controls section recording the attacks we ran that failed — the exact endpoints a lower-privilege user was refused, the cross-tenant replay that was rejected, the injection probes that found nothing, and how many of each we ran. That is the evidence a reviewer needs to accept the result, and it's what turns "no findings" from an empty page into a defensible assurance.

13 · Using the report as audit evidence

What SOC 2 and ISO 27001 actually require, and where the report satisfies it.

A point of precision most vendors gloss over. There is no such thing as a "SOC 2 compliant" or "ISO 27001 certified" report — those frameworks certify organisations, not documents. What they require is that you test for vulnerabilities and act on what you find. A penetration test report is the evidence that you did. Anyone selling you a "SOC 2 certified report" is describing something that does not exist.

The controls this report speaks to

FrameworkControlWhat it requiresWhere the report answers it
ISO/IEC 27001:2022Annex A 8.8Management of technical vulnerabilities — identify, evaluate, actFindings with severity, business impact and remediation; re-test after fixes
ISO/IEC 27001:2022Annex A 8.29Security testing in development and acceptanceScope & methodology (Appendix A) and the dated assessment record
SOC 2 (TSC)CC7.1Detect and monitor for new vulnerabilitiesScheduled re-scans plus the assessment history in your dashboard
SOC 2 (TSC)CC4.1Ongoing evaluation of controlsVerified Controls — the attacks attempted that failed
PCI DSS v4.011.4External/internal penetration testingPartially — see the caveat below

What your auditor will ask for — and where it is

  • "What was in scope?" → Appendix A, with the exact verified hosts and the tier used.
  • "What methodology?" → Appendix A, plus this page in full.
  • "When was it performed?" → Report header and every finding's Identified on date.
  • "What did you find, and how bad?" → Master findings table with severity and state.
  • "What did you do about it?" → Remediation per finding, then a re-test showing the state change to Resolved.
  • "How do you know the controls work?" → Verified Controls: the attacks that were attempted and refused, with counts. This is the question most reports cannot answer.
  • "Who performed it?" → Aegis, automated — stated plainly. See the caveat.

The caveat you should hear from us, not your auditor.

Aegis is automated testing. Many auditors accept automated penetration-test evidence for SOC 2 and ISO 27001 — but your auditor decides, and some engagements specifically require a named, qualified human tester. That is common for PCI DSS 11.4 and in government or defence procurement. If yours requires a human, no automated product — ours or anyone's — satisfies it, and you should hire a consultancy. Ask your auditor before you buy; we would rather lose the sale than have you fail an audit holding our report.

14 · What we don't test — and why that matters

Stated plainly, because a vendor who claims to test everything is telling you something untrue.

Not testedWhy
Denial-of-service, load, stressDeliberately excluded — the risk of harming your service outweighs the finding.
Provider infrastructure (Cloudflare, AWS, DigitalOcean…)Not yours to authorise. Blocked in code.
Social engineering & phishingTargets people, not systems. Needs a human-led engagement.
Physical securityOut of scope for an application test.
Source-code review (SAST), dependencies (SCA), containersA different discipline. We test the running application from the outside, as an attacker meets it.
Novel zero-day discoveryRequires human creativity. Automation tests known and structural classes exceptionally well; it does not invent new attacks.

The honest bottom line. No automated test proves the absence of vulnerabilities. A clean Aegis report means the checks we ran — documented above, in full — found nothing. That is genuinely valuable evidence, and it is not the same as "your system is secure." Any vendor implying otherwise is selling you certainty that does not exist.