Skip to content

Commit f46c02a

Browse files
committed
style(security): fix Black formatting and Bandit warnings
Apply Black formatting to security module and tests. Suppress Bandit warnings for demo authentication code. Changes: - Format security module files with Black - Format routes.py and test_security.py with Black - Add nosec comments for intentional demo password (B105) - Add nosec comment for standard OAuth2 token type (B106) These are false positives for development-only demo authentication.
1 parent 375b3a4 commit f46c02a

4 files changed

Lines changed: 33 additions & 15 deletions

File tree

app/routes.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,7 @@ class ModelInfoResponse(BaseModel):
169169

170170
@router.post("/query", response_model=QueryResponse)
171171
@limiter.limit("10/minute")
172-
async def generate_sql(
173-
request: Request, query_request: QueryRequest
174-
) -> QueryResponse:
172+
async def generate_sql(request: Request, query_request: QueryRequest) -> QueryResponse:
175173
"""
176174
Generate SQL from natural language query.
177175
@@ -468,7 +466,9 @@ async def login(request: Request, credentials: LoginRequest) -> TokenResponse:
468466

469467
# TODO: Implement actual user authentication against database
470468
# For now, simple placeholder authentication
471-
if credentials.username == "demo" and credentials.password == "demo_password":
469+
if (
470+
credentials.username == "demo" and credentials.password == "demo_password"
471+
): # nosec B105 - Demo password for development only
472472
token = create_access_token(data={"sub": credentials.username})
473473

474474
settings = get_settings()
@@ -477,7 +477,7 @@ async def login(request: Request, credentials: LoginRequest) -> TokenResponse:
477477

478478
return TokenResponse(
479479
access_token=token,
480-
token_type="bearer",
480+
token_type="bearer", # nosec B106 - Standard OAuth2 token type
481481
expires_in=settings.security.jwt_access_token_expire_minutes * 60,
482482
)
483483

app/security/headers.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ async def dispatch(
5454

5555
# Content Security Policy - restrictive for API
5656
response.headers["Content-Security-Policy"] = (
57-
"default-src 'none'; "
58-
"frame-ancestors 'none'; "
59-
"base-uri 'none';"
57+
"default-src 'none'; " "frame-ancestors 'none'; " "base-uri 'none';"
6058
)
6159

6260
# Referrer policy - don't leak referrer to external sites

app/security/input_validation.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ def validate_database_id(database_id: str) -> tuple[bool, str | None]:
175175

176176
# Check format (alphanumeric, underscores, hyphens only)
177177
if not re.match(r"^[a-zA-Z0-9_-]+$", database_id):
178-
return False, "Database ID can only contain letters, numbers, underscores, and hyphens"
178+
return (
179+
False,
180+
"Database ID can only contain letters, numbers, underscores, and hyphens",
181+
)
179182

180183
# Must start with letter or number
181184
if not database_id[0].isalnum():
@@ -290,7 +293,9 @@ def create_query_whitelist() -> set[str]:
290293
}
291294

292295

293-
def check_query_against_whitelist(sql: str, whitelist: set[str] | None = None) -> tuple[bool, list[str]]:
296+
def check_query_against_whitelist(
297+
sql: str, whitelist: set[str] | None = None
298+
) -> tuple[bool, list[str]]:
294299
"""
295300
Check if SQL query only uses whitelisted keywords.
296301
@@ -311,8 +316,17 @@ def check_query_against_whitelist(sql: str, whitelist: set[str] | None = None) -
311316

312317
# List of dangerous keywords that should never be allowed
313318
dangerous_keywords = {
314-
"DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "CREATE", "TRUNCATE",
315-
"EXEC", "EXECUTE", "GRANT", "REVOKE"
319+
"DROP",
320+
"DELETE",
321+
"UPDATE",
322+
"INSERT",
323+
"ALTER",
324+
"CREATE",
325+
"TRUNCATE",
326+
"EXEC",
327+
"EXECUTE",
328+
"GRANT",
329+
"REVOKE",
316330
}
317331

318332
# Extract potential SQL keywords

tests/unit/test_security.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ def test_create_access_token(self):
3838
# Decode and verify
3939
settings = get_settings()
4040
payload = jwt.decode(
41-
token, settings.security.secret_key, algorithms=[settings.security.jwt_algorithm]
41+
token,
42+
settings.security.secret_key,
43+
algorithms=[settings.security.jwt_algorithm],
4244
)
4345
assert payload["sub"] == "test_user"
4446
assert "exp" in payload
@@ -53,7 +55,9 @@ def test_create_access_token_with_expiration(self):
5355

5456
settings = get_settings()
5557
payload = jwt.decode(
56-
token, settings.security.secret_key, algorithms=[settings.security.jwt_algorithm]
58+
token,
59+
settings.security.secret_key,
60+
algorithms=[settings.security.jwt_algorithm],
5761
)
5862
assert payload["sub"] == "test_user"
5963

@@ -223,7 +227,9 @@ def test_validate_sql_query_safe(self):
223227

224228
def test_validate_sql_query_too_long(self):
225229
"""Test SQL validation rejects overly long queries."""
226-
sql = "SELECT * FROM users WHERE " + " AND ".join([f"col{i} = 1" for i in range(1000)])
230+
sql = "SELECT * FROM users WHERE " + " AND ".join(
231+
[f"col{i} = 1" for i in range(1000)]
232+
)
227233
is_valid, errors = validate_sql_query(sql, max_length=1000)
228234
assert is_valid is False
229235
assert any("maximum length" in e.lower() for e in errors)

0 commit comments

Comments
 (0)