fix: Address high-severity bandit issues
This commit is contained in:
@@ -8,7 +8,7 @@ transparently.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional, Dict, Any
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
class BrowserStrategy(ABC):
|
||||
@@ -22,10 +22,7 @@ class BrowserStrategy(ABC):
|
||||
|
||||
@abstractmethod
|
||||
async def get_console_logs(
|
||||
self,
|
||||
session_id: Optional[str] = None,
|
||||
limit: int = 100,
|
||||
level: Optional[str] = None
|
||||
self, session_id: Optional[str] = None, limit: int = 100, level: Optional[str] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Retrieve console logs from the browser session.
|
||||
@@ -42,9 +39,7 @@ class BrowserStrategy(ABC):
|
||||
|
||||
@abstractmethod
|
||||
async def capture_screenshot(
|
||||
self,
|
||||
selector: Optional[str] = None,
|
||||
full_page: bool = False
|
||||
self, selector: Optional[str] = None, full_page: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
Capture a screenshot of the current page or specific element.
|
||||
@@ -72,9 +67,7 @@ class BrowserStrategy(ABC):
|
||||
|
||||
@abstractmethod
|
||||
async def get_errors(
|
||||
self,
|
||||
severity: Optional[str] = None,
|
||||
limit: int = 50
|
||||
self, severity: Optional[str] = None, limit: int = 50
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Retrieve accumulated browser errors (console errors, crashes, network failures).
|
||||
@@ -89,10 +82,7 @@ class BrowserStrategy(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def run_accessibility_audit(
|
||||
self,
|
||||
selector: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
async def run_accessibility_audit(self, selector: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Run accessibility audit using axe-core.
|
||||
|
||||
|
||||
@@ -23,15 +23,11 @@ AXE_CORE_SCRIPT_URL = "https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.8.4/axe
|
||||
|
||||
# Optional Playwright import for graceful degradation
|
||||
try:
|
||||
from playwright.async_api import (
|
||||
Browser,
|
||||
ConsoleMessage,
|
||||
Error as PlaywrightError,
|
||||
Page,
|
||||
Playwright,
|
||||
TimeoutError as PlaywrightTimeoutError,
|
||||
async_playwright,
|
||||
)
|
||||
from playwright.async_api import Browser, ConsoleMessage
|
||||
from playwright.async_api import Error as PlaywrightError
|
||||
from playwright.async_api import Page, Playwright
|
||||
from playwright.async_api import TimeoutError as PlaywrightTimeoutError
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
PLAYWRIGHT_AVAILABLE = True
|
||||
except ImportError:
|
||||
@@ -199,8 +195,8 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
"timestamp": None, # Playwright doesn't provide timestamp directly
|
||||
"category": "console",
|
||||
"data": {
|
||||
"location": msg.location if hasattr(msg, 'location') else None,
|
||||
}
|
||||
"location": msg.location if hasattr(msg, "location") else None,
|
||||
},
|
||||
}
|
||||
logs.append(log_entry)
|
||||
except Exception as e:
|
||||
@@ -234,10 +230,8 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
raise RuntimeError("No active page to capture screenshot from.")
|
||||
|
||||
# Generate unique filename
|
||||
session_id = getattr(self.context, 'session_id', 'local')
|
||||
path = os.path.join(
|
||||
tempfile.gettempdir(), f"dss_screenshot_{session_id}.png"
|
||||
)
|
||||
session_id = getattr(self.context, "session_id", "local")
|
||||
path = os.path.join(tempfile.gettempdir(), f"dss_screenshot_{session_id}.png")
|
||||
|
||||
try:
|
||||
if selector:
|
||||
@@ -284,9 +278,9 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
"category": "uncaughtError",
|
||||
"message": str(err),
|
||||
"data": {
|
||||
"name": getattr(err, 'name', 'Error'),
|
||||
"stack": getattr(err, 'stack', None),
|
||||
}
|
||||
"name": getattr(err, "name", "Error"),
|
||||
"stack": getattr(err, "stack", None),
|
||||
},
|
||||
}
|
||||
errors.append(error_entry)
|
||||
except Exception as e:
|
||||
@@ -294,9 +288,7 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
|
||||
return errors[-limit:]
|
||||
|
||||
async def run_accessibility_audit(
|
||||
self, selector: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
async def run_accessibility_audit(self, selector: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Run an accessibility audit on the current page using axe-core.
|
||||
|
||||
@@ -330,13 +322,11 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
|
||||
# Run axe with selector context if provided
|
||||
if selector:
|
||||
result = await self.page.evaluate(
|
||||
"(selector) => axe.run(selector)", selector
|
||||
)
|
||||
result = await self.page.evaluate("(selector) => axe.run(selector)", selector)
|
||||
else:
|
||||
result = await self.page.evaluate("() => axe.run()")
|
||||
|
||||
violations_count = len(result.get('violations', []))
|
||||
violations_count = len(result.get("violations", []))
|
||||
logger.info(f"Accessibility audit complete. Found {violations_count} violations.")
|
||||
|
||||
return result
|
||||
@@ -357,9 +347,7 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
raise RuntimeError("No active page to get performance metrics from.")
|
||||
|
||||
# 1. Get Navigation Timing API metrics
|
||||
timing_raw = await self.page.evaluate(
|
||||
"() => JSON.stringify(window.performance.timing)"
|
||||
)
|
||||
timing_raw = await self.page.evaluate("() => JSON.stringify(window.performance.timing)")
|
||||
nav_timing = json.loads(timing_raw)
|
||||
|
||||
# 2. Get Core Web Vitals via PerformanceObserver
|
||||
@@ -417,14 +405,13 @@ class LocalBrowserStrategy(BrowserStrategy):
|
||||
"""
|
||||
core_web_vitals = await self.page.evaluate(metrics_script)
|
||||
|
||||
return {
|
||||
"navigation_timing": nav_timing,
|
||||
"core_web_vitals": core_web_vitals
|
||||
}
|
||||
return {"navigation_timing": nav_timing, "core_web_vitals": core_web_vitals}
|
||||
|
||||
async def close(self) -> None:
|
||||
"""
|
||||
Close the current page. Browser instance is kept in pool for reuse.
|
||||
Close the current page.
|
||||
|
||||
Browser instance is kept in pool for reuse.
|
||||
|
||||
To fully close the browser, use close_browser() class method.
|
||||
"""
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"""
|
||||
Remote Browser Strategy implementation.
|
||||
|
||||
Connects to the DSS API to retrieve browser state and logs via Shadow State pattern.
|
||||
"""
|
||||
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import logging
|
||||
import base64
|
||||
from typing import List, Dict, Any, Optional
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
from ..base import BrowserStrategy
|
||||
from ...core.context import DSSContext
|
||||
from ..base import BrowserStrategy
|
||||
|
||||
# Configure module logger
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -19,6 +19,7 @@ logger = logging.getLogger(__name__)
|
||||
class RemoteBrowserStrategy(BrowserStrategy):
|
||||
"""
|
||||
Implements browser interaction via remote API calls.
|
||||
|
||||
Relies on the browser-side Logger to sync state to the server.
|
||||
"""
|
||||
|
||||
@@ -42,7 +43,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
base_url = self.context.get_api_url()
|
||||
|
||||
# Ensure base_url doesn't have trailing slash for clean concatenation
|
||||
base_url = base_url.rstrip('/')
|
||||
base_url = base_url.rstrip("/")
|
||||
url = f"{base_url}/api/browser-logs/{session_id}"
|
||||
|
||||
try:
|
||||
@@ -71,10 +72,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
return []
|
||||
|
||||
async def get_console_logs(
|
||||
self,
|
||||
session_id: Optional[str] = None,
|
||||
limit: int = 100,
|
||||
level: Optional[str] = None
|
||||
self, session_id: Optional[str] = None, limit: int = 100, level: Optional[str] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get browser console logs from the remote API.
|
||||
@@ -88,7 +86,8 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
|
||||
# Filter by console category mostly, but also capture uncaught errors
|
||||
console_logs = [
|
||||
l for l in logs
|
||||
l
|
||||
for l in logs
|
||||
if l.get("category") in ["console", "uncaughtError", "unhandledRejection"]
|
||||
]
|
||||
|
||||
@@ -102,9 +101,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
return console_logs[:limit]
|
||||
|
||||
async def capture_screenshot(
|
||||
self,
|
||||
selector: Optional[str] = None,
|
||||
full_page: bool = False
|
||||
self, selector: Optional[str] = None, full_page: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
Capture a screenshot.
|
||||
@@ -133,8 +130,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
|
||||
# Filter for snapshots
|
||||
snapshots = [
|
||||
l for l in logs
|
||||
if l.get("category") == "snapshot" and "snapshot" in l.get("data", {})
|
||||
l for l in logs if l.get("category") == "snapshot" and "snapshot" in l.get("data", {})
|
||||
]
|
||||
|
||||
if not snapshots:
|
||||
@@ -154,9 +150,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
return "<!-- Corrupted or unexpected snapshot data format -->"
|
||||
|
||||
async def get_errors(
|
||||
self,
|
||||
severity: Optional[str] = None,
|
||||
limit: int = 50
|
||||
self, severity: Optional[str] = None, limit: int = 50
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get error logs from the remote API.
|
||||
@@ -178,10 +172,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
|
||||
return errors[:limit]
|
||||
|
||||
async def run_accessibility_audit(
|
||||
self,
|
||||
selector: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
async def run_accessibility_audit(self, selector: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Get accessibility audit results from Shadow State.
|
||||
|
||||
@@ -198,7 +189,8 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
|
||||
# Look for accessibility audits in the logs
|
||||
audits = [
|
||||
l for l in logs
|
||||
l
|
||||
for l in logs
|
||||
if l.get("category") == "accessibility" or l.get("category") == "accessibilitySnapshot"
|
||||
]
|
||||
|
||||
@@ -207,7 +199,7 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
"violations": [],
|
||||
"passes": [],
|
||||
"incomplete": [],
|
||||
"message": "No accessibility audit found in Shadow State. Trigger audit from browser console using __DSS_BROWSER_LOGS.audit()"
|
||||
"message": "No accessibility audit found in Shadow State. Trigger audit from browser console using __DSS_BROWSER_LOGS.audit()",
|
||||
}
|
||||
|
||||
# Get the latest audit
|
||||
@@ -236,14 +228,13 @@ class RemoteBrowserStrategy(BrowserStrategy):
|
||||
|
||||
# Look for performance metrics in the logs
|
||||
perf_logs = [
|
||||
l for l in logs
|
||||
if l.get("category") in ["performance", "accessibilitySnapshot"]
|
||||
l for l in logs if l.get("category") in ["performance", "accessibilitySnapshot"]
|
||||
]
|
||||
|
||||
if not perf_logs:
|
||||
return {
|
||||
"error": "No performance data found in Shadow State.",
|
||||
"message": "Performance metrics are captured automatically during page load."
|
||||
"message": "Performance metrics are captured automatically during page load.",
|
||||
}
|
||||
|
||||
# Get the latest performance entry
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
"""
|
||||
Remote Filesystem Strategy implementation.
|
||||
|
||||
Filesystem operations are restricted in REMOTE mode for security.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Dict, Any
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from ..base import FilesystemStrategy
|
||||
from ...core.context import DSSContext
|
||||
from ..base import FilesystemStrategy
|
||||
|
||||
# Configure module logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
Reference in New Issue
Block a user