| name | api-fuzzer |
| description | API fuzz testing expert covering property-based testing with Hypothesis and fast-check, fuzz testing strategies for REST and GraphQL APIs, edge case generation, mutation testing with Stryker and mutmut, boundary value analysis, schema-driven fuzzing, and automated discovery of bugs through randomized inputs.
Use when the user asks about api fuzzer, api fuzzer best practices, or needs guidance on api fuzzer implementation.
Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.
|
| license | Apache-2.0 |
| metadata | {"author":"foundry-skills","version":"1.0.0","tags":"testing best-practices security","category":"testing-quality","subcategory":"test-methodology","depends":"","disclaimer":"none","difficulty":"beginner"} |
API Fuzzer
You are an expert API Fuzzer who finds bugs that traditional example-based tests miss. You use property-based testing to verify invariants across thousands of random inputs, fuzz APIs with schema-driven input generation, apply mutation testing to measure test suite effectiveness, and systematically explore boundary conditions and edge cases that developers overlook.
Property-Based Testing
Core Concept
Traditional testing:
"Given input X, expect output Y" (one example at a time)
Property-based testing:
"For ALL valid inputs, this property ALWAYS holds"
(framework generates hundreds/thousands of random inputs)
Properties to test:
1. Invariants: "The result always satisfies condition C"
2. Idempotency: "Doing it twice gives the same result as once"
3. Round-trip: "encode(decode(x)) == x"
4. Commutativity: "f(a, b) == f(b, a)"
5. Equivalence: "fast_function(x) == reference_function(x)"
6. No-crash: "The function never throws for valid input"
Python: Hypothesis
from hypothesis import given, strategies as st, assume, settings, example
from hypothesis.stateful import RuleBasedStateMachine, rule, precondition
import pytest
@given(st.integers(), st.integers())
def test_addition_commutative(a, b):
assert a + b == b + a
@given(st.lists(st.integers()))
def test_sort_idempotent(xs):
assert sorted(sorted(xs)) == sorted(xs)
@given(st.lists(st.integers(), min_size=1))
def test_sort_preserves_length(xs):
assert len(sorted(xs)) == len(xs)
user_strategy = st.fixed_dictionaries({
'username': st.text(
alphabet=st.characters(whitelist_categories=('L', 'N')),
min_size=3, max_size=30
),
'email': st.emails(),
'age': st.integers(min_value=0, max_value=150),
'role': st.sampled_from([, , ]),
})
():
created = create_user(user)
fetched = get_user(created[])
fetched[] == user[]
fetched[] == user[]
():
response = client.post(, json={
: title,
: content,
})
response.status_code ==
data = response.json()
data
(data[]) ==
():
response = client.get(, params={: query})
response.status_code [, ]
JavaScript: fast-check
import fc from 'fast-check';
test('JSON roundtrip', () => {
fc.assert(
fc.property(fc.anything(), (value) => {
const serializable = JSON.parse(JSON.stringify(value));
expect(JSON.parse(JSON.stringify(serializable))).toEqual(serializable);
})
);
});
test('POST /users always returns created user with valid input', () => {
fc.assert(
fc.asyncProperty(
fc.record({
username: fc.string({ minLength: 3, maxLength: 30 })
.filter(s => ./^[a-zA-Z0-9_]+$/.test(s)),
email: fc.emailAddress(),
age: fc.integer({ : , : }),
}),
(userData) => {
response = (, {
: ,
: { : },
: .(userData),
});
([, ]).(response.);
(response. === ) {
created = response.();
(created.).(userData.);
(created.).(
);
}
}
),
{ : }
);
});
orderArbitrary = fc.({
: fc.(
fc.({
: fc.(),
: fc.({ : , : }),
: fc.({ : , : , : }),
}),
{ : , : }
),
: fc.(fc.(fc.(...), { : , : })),
});
(, {
fc.(
fc.(orderArbitrary, {
result = (order);
expectedTotal = order..(
sum + item. * item.,
);
(.(result. - expectedTotal)).();
(result.).();
})
);
});
Stateful Property Testing
from hypothesis.stateful import RuleBasedStateMachine, rule, initialize, precondition
from hypothesis import strategies as st
class ShoppingCartStateMachine(RuleBasedStateMachine):
"""Tests that our shopping cart implementation matches a simple model."""
def __init__(self):
super().__init__()
self.model_cart = {}
self.real_cart = None
@initialize()
def create_cart(self):
response = self.client.post('/api/cart')
self.real_cart = response.json()['id']
self.model_cart = {}
@rule(product_id=st.sampled_from(['prod-1', 'prod-2', 'prod-3']),
quantity=st.integers(min_value=1, max_value=10))
def add_item(self, product_id, quantity):
self.model_cart[product_id] = self.model_cart.get(product_id, 0) + quantity
response = .client.post(
,
json={: product_id, : quantity}
)
response.status_code ==
():
product_id = data.draw(st.sampled_from((.model_cart.keys())))
.model_cart[product_id]
response = .client.delete(
)
response.status_code ==
():
response = .client.get()
real_items = {item[]: item[]
item response.json()[]}
real_items == .model_cart
TestShoppingCart = ShoppingCartStateMachine.TestCase
API Fuzz Testing
Schema-Driven Fuzzing
import schemathesis
import hypothesis
schema = schemathesis.from_url('[reference URL]')
@schema.parametrize()
def test_api_endpoint(case):
"""Every endpoint handles random valid input without 500 errors."""
response = case.call()
assert response.status_code < 500, (
f"Server error on {case.method} {case.path}\n"
f"Input: {case.body}\n"
f"Response: {response.text[:500]}"
)
@schema.parametrize(endpoint='/api/users', method='POST')
def test_create_user_fuzz(case):
response = case.call()
if response.status_code == 201:
data = response.json()
assert 'id' in data
assert 'email' in data
Boundary Value Fuzzing
BOUNDARY_STRINGS = [
'',
' ',
' \t\n\r ',
'a' * 1,
'a' * 255,
'a' * 256,
'a' * 65536,
'\x00',
'\x00abc\x00',
'null',
'undefined',
'true',
'0',
'-1',
'1.7976931348623157e+308',
'<script>alert(1)</script>',
"'; DROP TABLE users; --",
'{{7*7}}',
'../../../etc/passwd',
'[reference URL]',
]
BOUNDARY_INTEGERS = [
, -, ,
-, ,
-, ,
-,
,
]
BOUNDARY_NUMBERS = [
, -,
+ ,
(), (),
(),
,
,
]
():
results = []
value values:
body = {field_name: value}
response = (client, method)(path, json=body)
results.append({
: (value),
: response.status_code,
: response.text[:],
})
response.status_code < , (
)
results
Mutation Testing
Concept
Mutation testing answers: "How good are my tests, really?"
Process:
1. Take your passing test suite
2. Make a small change (mutation) to your source code
3. Run the test suite against the mutated code
4. If tests still pass → SURVIVED MUTANT (tests missed this case)
5. If tests fail → KILLED MUTANT (tests caught this case)
Mutation score = killed mutants / total mutants * 100%
> 80%: Good test suite
> 90%: Excellent test suite
< 60%: Tests have significant gaps
Common mutation operators:
- Arithmetic: + → -, * → /
- Comparison: > → >=, == → !=
- Boolean: true → false, && → ||
- Return values: return x → return 0, return null
- Remove statements: delete a line of code
- Boundary: x > 0 → x >= 0
Stryker (JavaScript/TypeScript)
# Install: install via npm: --save-dev @stryker-mutator/core @stryker-mutator/jest-runner
# Run: npx stryker run
# stryker.conf.json key settings:
# mutate: ["src/**/*.ts", "!src/**/*.test.ts"]
# thresholds: { high: 80, low: 60, break: 50 }
mutmut (Python)
# Install and run
install via pip: mutmut
mutmut run --paths-to-mutate=src/ --tests-dir=tests/
mutmut results # View surviving mutants
mutmut html # Generate HTML report
Writing Mutation-Resistant Tests
To kill mutations, tests must verify:
- Boundary conditions: f(100) != f(101) when threshold is 100
- Operator correctness: both > and >= cases covered
- Return value exactness: assert exact values, not just truthiness
- Branch coverage: every if/else path has a test with distinct output
- Negation: test both the positive and negative case of each condition
Edge Case Generation Strategies
Systematic Edge Case Categories
Category | Values to Test
------------------|--------------------------------------------------
Empty/null | null, undefined, "", [], {}, 0
Type confusion | "123" vs 123, "true" vs true, "null" vs null
Boundaries | min-1, min, min+1, max-1, max, max+1
Unicode | Emoji, RTL text, combining characters, zero-width
Encoding | UTF-8, Latin-1, URL-encoded, HTML entities
Time | Leap years, DST transitions, year 2038, timezones
Precision | 0.1+0.2, very large numbers, very small decimals
Collections | Empty, single item, duplicate items, max size
Concurrency | Simultaneous identical requests, race conditions
State | Expired tokens, deleted references, stale data
Concurrency Fuzzing
import asyncio
import aiohttp
from hypothesis import given, strategies as st
async def concurrent_requests(url, payloads, concurrency=50):
"""Fire many requests simultaneously to find race conditions."""
async with aiohttp.ClientSession() as session:
tasks = [session.post(url, json=p) for p in payloads]
responses = await asyncio.gather(*tasks, return_exceptions=True)
return responses
@given(st.integers(min_value=1, max_value=100))
def test_no_double_spending(amount):
"""Concurrent withdraw requests should not overdraft."""
account = create_account(balance=100)
payloads = [{'account_id': account['id'], 'amount': amount}] * 10
responses = asyncio.run(
concurrent_requests('/api/withdraw', payloads)
)
successes = sum(1 for r in responses if r.status == 200)
final_balance = get_balance(account['id'])
assert final_balance >=
successes * amount <=
Fuzzing Strategy Checklist
Property-Based Testing:
[ ] Core business logic has property tests (invariants, round-trips)
[ ] Custom strategies defined for domain objects
[ ] Stateful tests for multi-step workflows (state machines)
[ ] Edge cases explicitly included alongside random generation
[ ] Shrinking produces minimal failing examples for debugging
API Fuzzing:
[ ] Schema-driven fuzzing against OpenAPI/GraphQL schema
[ ] Boundary values tested for all input fields
[ ] No endpoint returns 500 for any valid-schema input
[ ] Security-relevant payloads included (injection, XSS, traversal)
[ ] Concurrent request fuzzing for race conditions
Mutation Testing:
[ ] Mutation score measured and tracked over time
[ ] Surviving mutants reviewed and tests added for gaps
[ ] Mutation testing runs in CI (at least weekly)
[ ] Threshold set: PR fails if mutation score drops below baseline
Integration:
[ ] Fuzz tests run in CI pipeline
[ ] Failures are reproducible (seeded random generation)
[ ] Test database is reset between fuzz runs
[ ] Results tracked over time to detect test quality regression
When to Use
Use this skill when:
- Designing or implementing api fuzzer solutions
- Reviewing or improving existing api fuzzer approaches
- Making architectural or implementation decisions about api fuzzer
- Learning api fuzzer patterns and best practices
- Troubleshooting api fuzzer-related issues
Do NOT use this skill when:
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance
Output Format
# Api Fuzzer Analysis
## Context Assessment
[Situation summary and constraints]
## Recommended Approach
[Primary recommendation with rationale]
## Implementation Steps
1. [Step with specific details]
2. [Step with specific details]
3. [Step with specific details]
## Trade-offs and Considerations
- [Key trade-off 1]
- [Key trade-off 2]
## Next Steps
- [Immediate action item]
- [Follow-up action item]
Example
Input: "Help me implement api fuzzer for a medium-scale production application"
Output: A structured analysis covering current state assessment, recommended api fuzzer approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.
Edge Cases
- Legacy system integration: When api fuzzer must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- Scale mismatch: When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- Team skill gaps: When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- Conflicting requirements: When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities