From harness-claude
Identifies race conditions and TOCTOU vulnerabilities in concurrent operations like financial transactions, auth flows, inventory, file ops, and DB locking strategies.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> When security depends on the order of operations but the system does not enforce that
Flags race conditions (CWE-362) in check-then-act sequences on shared state, file operations, balance updates, and counters. Suggests atomic fixes with locks, transactions, CAS.
Tests web apps for race conditions, single-packet attacks, TOCTOU vulnerabilities, double-spends, rate limit bypasses, and concurrency issues via endpoint analysis and HTTP/2 manifests.
Detects race conditions in PHP code including check-then-act patterns, TOCTOU vulnerabilities, shared mutable state, read-modify-write issues, file/DB/session races. Includes grep patterns for scanning.
Share bugs, ideas, or general feedback.
When security depends on the order of operations but the system does not enforce that order, attackers exploit the gap between check and use -- turning microsecond timing windows into privilege escalation, double-spend, and data corruption
Race conditions are among the most underestimated vulnerability classes because they are non-deterministic -- they may not appear in testing but are reliably exploitable by an attacker who controls timing:
madvise(MADV_DONTNEED)), the attacker could
write to files they had only read permission for, including /etc/passwd. This privilege
escalation vulnerability existed in the Linux kernel for 9 years before discovery./etc/shadow. The privileged program writes to the
symlink, overwriting the password file. This classic TOCTOU pattern has affected sudo,
sendmail, and numerous other privileged Unix programs.Recognize the TOCTOU pattern. Time-of-check-to-time-of-use vulnerabilities follow a universal structure: the program checks a condition (file exists, balance is sufficient, user is authorized, code is unused), then performs an action based on that condition (write the file, deduct the balance, grant access, apply the discount). If the condition can change between the check and the action, the action operates on a stale assumption. The fix is always the same principle: make the check and the action atomic -- either they happen as one indivisible operation, or a lock ensures nothing changes between them.
Use database-level atomicity for business logic. The database is the only reliable point of serialization in a multi-instance web application:
SELECT balance FROM accounts WHERE id = ? FOR UPDATE, verify
balance is sufficient, then UPDATE accounts SET balance = balance - ? WHERE id = ?.SELECT to see if exists, then INSERT).pg_advisory_lock in PostgreSQL,
GET_LOCK in MySQL) for coordinating operations that span multiple tables or require
application-level logic between the check and the action.UPDATE accounts SET balance = balance - 100 WHERE id = ? AND balance >= 100 -- this combines the check and the update in a single atomic statement.
If the balance is insufficient, zero rows are updated. No race window exists.Implement idempotency keys for external-facing operations. Idempotency ensures that processing the same request multiple times produces the same result as processing it once:
Use optimistic locking for low-contention updates. Add a version column to the row.
Every update includes WHERE version = ? with the version the client read. If another
transaction modified the row (incrementing the version), the update affects zero rows and
the application knows to retry with the new version:
SELECT id, balance, version FROM accounts WHERE id = ?UPDATE accounts SET balance = ?, version = version + 1 WHERE id = ? AND version = ?Eliminate file system race conditions. File operations are particularly vulnerable because the file system namespace is shared:
O_CREAT | O_EXCL flags with open(). This creates the
file only if it does not already exist, atomically. If the file exists (including as a
symlink), the call fails. Do not use access() or stat() followed by open().fstat(),
fchmod(), fchown() instead of stat(), chmod(), chown(). File descriptor
operations act on the actual file, not the path -- an attacker cannot redirect them via
symlink replacement.mkstemp() or tmpfile(): These create temporary files with unique names
atomically. Do not use tmpnam() or tempnam() followed by open() -- the name can
be predicted and replaced between generation and use.renameat2() with RENAME_NOREPLACE (Linux) for atomic file placement.
Write content to a temporary file, then atomically rename it to the target path. The
rename is atomic -- readers see either the old file or the new file, never a partial
write.Design for concurrent execution from the start. Assume every operation will be executed concurrently by multiple instances:
How attackers exploit web application race conditions at scale: Tools like Turbo Intruder (Burp Suite extension) and custom scripts send dozens to hundreds of concurrent HTTP requests, all arriving at the server within a few milliseconds. The attacker does not need precise timing -- they flood the server with concurrent requests and statistical probability ensures that some requests hit the race window. A discount code that can be used once is used 50 times. A bank transfer of $100 from a $150 account is executed 10 times. A free trial activation creates 100 accounts. The defense must be correct under maximum concurrency, not just typical load.
Database isolation levels and their race condition implications: READ UNCOMMITTED allows
dirty reads (seeing uncommitted changes from other transactions). READ COMMITTED (the
default in PostgreSQL and Oracle) prevents dirty reads but allows non-repeatable reads (a
row read twice in the same transaction returns different values) and phantom reads (a range
query returns different rows). REPEATABLE READ (the default in MySQL/InnoDB) prevents dirty
and non-repeatable reads but allows phantom reads in the SQL standard (though InnoDB's
implementation also prevents phantoms). SERIALIZABLE prevents all anomalies but increases
contention. For financial transactions, READ COMMITTED with explicit SELECT FOR UPDATE
locking is the most common practical approach -- it provides the necessary atomicity
without the global performance cost of serializable isolation.
The double-spend pattern in detail: User has $150. Two concurrent transfers of $100
each arrive. Thread A: SELECT balance FROM accounts WHERE id = 1 returns $150. Thread B:
SELECT balance FROM accounts WHERE id = 1 returns $150 (Thread A has not yet updated).
Thread A: balance >= 100, so UPDATE accounts SET balance = 50 WHERE id = 1. Thread B:
balance >= 100 (it read $150), so UPDATE accounts SET balance = 50 WHERE id = 1. Both
succeed. The user transferred $200 from a $150 balance. With SELECT FOR UPDATE, Thread B
blocks until Thread A commits, then re-reads balance as $50 and correctly rejects the
transfer.
Check-then-act without holding a lock. Reading a value, making a decision based on that value, and then writing -- without ensuring the value has not changed. This is the fundamental race condition pattern. The gap between the check and the act is the vulnerability window. Use atomic operations, database locks, or optimistic locking to close the window.
Using READ COMMITTED isolation for financial transactions without explicit locking.
READ COMMITTED does not prevent two transactions from reading the same balance and both
deducting from it. Without SELECT FOR UPDATE or an atomic UPDATE ... WHERE balance >= ?, the double-spend pattern is exploitable. READ COMMITTED is safe for reads that do not
influence writes, but financial transactions require explicit serialization.
Relying on application-level uniqueness checks without database constraints. Checking
SELECT COUNT(*) FROM users WHERE email = ? and then INSERT INTO users (email, ...) VALUES (?, ...) if the count is zero. Two concurrent registrations with the same email
both read count = 0 and both insert. Add a UNIQUE constraint on the email column and
handle the constraint violation error -- the database enforces uniqueness atomically.
Non-atomic file operations with predictable temporary filenames. Using tempnam() to
generate a filename, then open() to create it. An attacker predicts the filename (or
creates symlinks for all likely names) and interposes between generation and creation.
Use mkstemp() which generates and opens the file in a single atomic operation.
Assuming single-threaded execution in a multi-instance deployment. Code that uses
in-memory locks (synchronized in Java, threading.Lock in Python) to prevent race
conditions. These locks protect against concurrency within a single process but do nothing
when the application runs as multiple instances behind a load balancer. The lock must be
at the shared state level -- typically the database or a distributed lock (Redis SETNX,
ZooKeeper, etcd).