mishandling-exceptional-conditions — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited mishandling-exceptional-conditions (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Brand new category for 2025, replacing SSRF (absorbed into A01). Contains 24 CWEs with 769,581 recorded occurrences. Addresses programs that fail to prevent, detect, and respond to unusual situations — leading to crashes, unexpected behaviour, information disclosure, and exploitable states.
| Weakness | Risk |
|---|---|
| Improper error handling | Unhandled exceptions crash the app or expose internals |
| Failing open (CWE-636) | Auth/authz checks that pass when an exception is thrown |
| Sensitive data in error messages (CWE-209) | Stack traces, DB details, file paths leaked to clients |
| Uncaught exceptions (CWE-248) | No global safety net → 500 with debug output |
| Resource exhaustion in exception paths | DB connections, file handles never closed on error |
| State corruption | Partial transactions committed on exception |
| NULL pointer / None dereference (CWE-476) | Missing guard for None before attribute access |
| Missing default cases (CWE-754) | if/elif chains with no else; match with no wildcard |
| Race conditions in error recovery | Cleanup logic itself can be interrupted |
# REQUIRED — catches anything not handled locally
@app.exception_handler(Exception)
async def global_handler(request: Request, exc: Exception):
logger.error("Unhandled exception", exc_info=exc)
return JSONResponse(status_code=500, content={"detail": "Internal server error"})Flag if absent. The default FastAPI 500 response may include traceback in development mode.
# BAD — exception causes pass-through (fail open)
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
try:
verify_token(request)
except Exception:
pass # ← VULNERABILITY
return await call_next(request)
# GOOD — exception causes denial (fail closed)
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
try:
verify_token(request)
except Exception as e:
logger.warning("Auth check failed", exc_info=e)
return JSONResponse(status_code=401, content={"detail": "Unauthorized"})
return await call_next(request)# Register handlers for all common error codes
@app.exception_handler(404)
async def not_found(request, exc):
return JSONResponse(status_code=404, content={"detail": "Not found"})
@app.exception_handler(422)
async def validation_error(request, exc):
return JSONResponse(status_code=422, content={"detail": "Invalid input"})Flag any handler that returns `str(exc)`, `repr(exc)`, or `traceback.format_exc()` in the response body.
# BAD — connection leaked if query raises
conn = db.connect()
result = conn.execute(query) # raises → conn never closed
conn.close()
# GOOD — context manager guarantees cleanup
with db.connect() as conn:
result = conn.execute(query)Scan for: db.connect(), open(file), socket.socket() not inside a with block or try/finally.
# BAD — partial write committed on error
session.add(obj)
risky_operation() # raises → session still commits elsewhere
session.commit()
# GOOD — rollback on exception
try:
session.add(obj)
risky_operation()
session.commit()
except Exception:
session.rollback()
raiseFlag: any session.commit() not paired with session.rollback() in an except branch.
@app.errorhandler(Exception)
def global_error(e):
app.logger.error("Unhandled exception", exc_info=e)
return {"error": "Internal server error"}, 500Also register specific handlers:
@app.errorhandler(404)
def not_found(e):
return {"error": "Not found"}, 404
@app.errorhandler(500)
def server_error(e):
return {"error": "Server error"}, 500Debug mode (debug=True or FLASK_DEBUG=1) enables the Werkzeug interactive debugger, which:
Flag immediately if found in production config or environment variables.
request.args.get() Silent None Returns# BAD — returns None silently; downstream code may crash
user_id = request.args.get("user_id")
user = db.query(User).filter_by(id=user_id).first() # id=None → wrong query
# GOOD — explicit validation
user_id = request.args.get("user_id")
if user_id is None:
abort(400, description="user_id is required")FastAPI's Pydantic models prevent this automatically; Flask requires manual guards.
before_request Hooks# BAD — exception in auth check bypasses protection
@app.before_request
def check_auth():
try:
verify_session()
except:
pass # ← VULNERABILITY
# GOOD
@app.before_request
def check_auth():
try:
verify_session()
except Exception as e:
app.logger.warning("Session verify failed", exc_info=e)
abort(401)bandit -r . -t B110,B112 # try-except-pass and try-except-continue
bandit -r . --severity-level mediumKey Bandit test IDs for this category:
B110 — try: ... except: pass (swallowed exception)B112 — try: ... except: continue (swallowed in loop)# Fail-open patterns
rg "except.*:\s*(pass|continue|return True)" --type py
# Stack traces leaking to response
rg "traceback\.format_exc\(\)|str\(exc\)|repr\(exc\)" --type py
# Missing context managers
rg "open\(|\.connect\(\)" --type py # review each hit for 'with'
# Missing rollback
rg "session\.commit\(\)" --type py # check each for paired rollback401, not 200.| CWE | Name | Python Manifestation |
|---|---|---|
| CWE-209 | Sensitive Info in Error Messages | str(exc) in JSON response, debug traceback |
| CWE-248 | Uncaught Exception | No global handler; unhandled raise crashes app |
| CWE-636 | Failing Open | except: pass in auth check |
| CWE-703 | Improper Handling of Exceptional Conditions | Broad except that hides errors |
| CWE-754 | Improper Check for Unusual Conditions | Missing None/empty checks on external input |
| CWE-476 | NULL Pointer Dereference | Attribute access on potentially-None object |
| CWE-756 | Missing Custom Error Page | Default framework error pages with debug info |
before_request hooks (CWE-636) — direct authentication bypass.None returns from request.args.get() — logic bugs, not directly exploitable.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.