---
title: Findings
description: Every finding is a confirmed exploit with a reproduction. Here's how to read one.
---

grayhat reports only what it could break. Each finding on a pull request has the same parts.

## Anatomy of a finding

| Part | Example |
| --- | --- |
| Severity | `critical`, `high`, `medium`, `low` |
| Status | `EXPLOIT VERIFIED` |
| Weakness | `CWE-639` (authorization bypass through a user-controlled key) |
| Title | Any signed-in user can read any invoice |
| Cause | This PR removed `requireOwner()` from `GET /invoices/:id` |
| Proof of concept | The exact request that worked against the sandboxed app |
| Fix | A link to the fix PR, when one passed both checks |

## Severity

**critical**

Data from other users or tenants is exposed, or code runs that shouldn't. Fix before merging.

**high**

A real attack path with a narrower blast radius, such as a race that double-spends a balance.

**medium**

Exploitable, but needs an unusual precondition or an already-privileged user.

**low**

Hardening: the exploit worked, but the impact is minor.

## What it catches

Pattern scanners look for dangerous calls. The bugs that hurt most are usually a missing check, or two ordinary lines in the wrong order:

- **IDOR**: `GET /invoices/:id` returns anyone's invoice
- **Business logic**: coupons stack, or a refund exceeds the order total
- **Race conditions**: check-then-spend on a balance with no lock
- **Home-grown auth**: a token compared with `==` that never expires
- **Hidden code**: an encoded payload in a `postinstall` script

## Dismissing a finding

Reply to a finding to dismiss it with a reason. grayhat remembers the dismissal for the rest of that pull request and won't raise the same finding again on later pushes.
