release: nexus btpanel session fix and app-v2
This commit is contained in:
+136
@@ -0,0 +1,136 @@
|
||||
"""Full codebase audit scanner"""
|
||||
import sys, os, re
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
# === 1. Backend API endpoints ===
|
||||
print('=' * 60)
|
||||
print('1. BACKEND ROUTE REGISTRY')
|
||||
print('=' * 60)
|
||||
|
||||
api_files = [f for f in os.listdir('server/api') if f.endswith('.py') and f not in ('__init__.py', 'dependencies.py', 'schemas.py')]
|
||||
for f in sorted(api_files):
|
||||
try:
|
||||
with open(f'server/api/{f}', 'r', encoding='utf-8') as fh:
|
||||
content = fh.read()
|
||||
prefix_match = re.search(r'prefix=["\'](/api/[^"\']+)["\']', content)
|
||||
prefix = prefix_match.group(1) if prefix_match else '???'
|
||||
routes = re.findall(r'@router\.(get|post|put|delete|websocket)\(["\'](.*?)["\']', content)
|
||||
if routes:
|
||||
print(f' {f}: prefix={prefix}')
|
||||
for method, path in routes:
|
||||
print(f' {method.upper():4s} {prefix}{path}')
|
||||
except Exception as e:
|
||||
print(f' {f}: ERROR {e}')
|
||||
|
||||
# === 2. Frontend API calls ===
|
||||
print()
|
||||
print('=' * 60)
|
||||
print('2. FRONTEND API CALLS')
|
||||
print('=' * 60)
|
||||
|
||||
html_files = [f for f in os.listdir('web/app') if f.endswith('.html')]
|
||||
for f in sorted(html_files):
|
||||
try:
|
||||
with open(f'web/app/{f}', 'r', encoding='utf-8') as fh:
|
||||
content = fh.read()
|
||||
pattern = r"API\s*\+\s*['\"](/[^'\"]+)['\"]"
|
||||
calls = re.findall(pattern, content)
|
||||
if calls:
|
||||
print(f' {f}:')
|
||||
for call in sorted(set(calls)):
|
||||
print(f' {call}')
|
||||
except Exception as e:
|
||||
print(f' {f}: ERROR {e}')
|
||||
|
||||
# === 3. Missing imports (AuditLog used but not imported) ===
|
||||
print()
|
||||
print('=' * 60)
|
||||
print('3. POTENTIAL MISSING IMPORTS (AuditLog used without import)')
|
||||
print('=' * 60)
|
||||
|
||||
for f in sorted(api_files):
|
||||
try:
|
||||
with open(f'server/api/{f}', 'r', encoding='utf-8') as fh:
|
||||
content = fh.read()
|
||||
uses_audit = 'AuditLog(' in content
|
||||
imports_audit = 'from server.domain.models import' in content and 'AuditLog' in content.split('from server.domain.models import')[1].split('\n')[0] if 'from server.domain.models import' in content else False
|
||||
# Also check local imports
|
||||
local_imports_audit = 'from server.domain.models import AuditLog' in content or ('AuditLog' in content and 'import AuditLog' in content)
|
||||
if uses_audit and not imports_audit and not local_imports_audit:
|
||||
# Check if it's imported via local import inside function
|
||||
has_local = 'import AuditLog' in content or 'from server.domain.models import' in content
|
||||
if not has_local:
|
||||
print(f' [BUG] {f}: Uses AuditLog() but never imported!')
|
||||
else:
|
||||
print(f' [WARN] {f}: Uses AuditLog() via local import only')
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
# === 4. SSH pool usage (acquire without release) ===
|
||||
print()
|
||||
print('=' * 60)
|
||||
print('4. SSH POOL: acquire() without release()')
|
||||
print('=' * 60)
|
||||
|
||||
for root, dirs, files in os.walk('server'):
|
||||
for f in files:
|
||||
if not f.endswith('.py'):
|
||||
continue
|
||||
path = os.path.join(root, f)
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as fh:
|
||||
content = fh.read()
|
||||
acquires = len(re.findall(r'ssh_pool\.acquire', content))
|
||||
releases = len(re.findall(r'ssh_pool\.release', content))
|
||||
if acquires > 0 and acquires != releases:
|
||||
print(f' [WARN] {path}: acquire={acquires} release={releases}')
|
||||
except:
|
||||
pass
|
||||
|
||||
# === 5. AsyncSessionLocal without commit ===
|
||||
print()
|
||||
print('=' * 60)
|
||||
print('5. DB writes without commit()')
|
||||
print('=' * 60)
|
||||
|
||||
for root, dirs, files in os.walk('server'):
|
||||
for f in files:
|
||||
if not f.endswith('.py'):
|
||||
continue
|
||||
path = os.path.join(root, f)
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as fh:
|
||||
content = fh.read()
|
||||
has_create = 'repo.create(' in content or 'session.add(' in content or 'session.execute(update' in content
|
||||
has_commit = 'session.commit()' in content
|
||||
if has_create and not has_commit and 'api/' in path:
|
||||
print(f' [WARN] {path}: has DB writes but no session.commit()')
|
||||
except:
|
||||
pass
|
||||
|
||||
# === 6. Hardcoded secrets or IPs ===
|
||||
print()
|
||||
print('=' * 60)
|
||||
print('6. HARDCODED SECRETS / IPs')
|
||||
print('=' * 60)
|
||||
|
||||
for root, dirs, files in os.walk('server'):
|
||||
for f in files:
|
||||
if not f.endswith('.py'):
|
||||
continue
|
||||
path = os.path.join(root, f)
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as fh:
|
||||
lines = fh.readlines()
|
||||
for i, line in enumerate(lines, 1):
|
||||
if re.search(r'password\s*=\s*["\'][^"\']{3,}["\']', line) and 'test' not in line.lower() and 'hash' not in line.lower() and 'Column' not in line:
|
||||
print(f' [WARN] {path}:{i}: possible hardcoded password')
|
||||
if re.search(r'\b10\.0\.\d+\.\d+\b|\b192\.168\.\d+\.\d+\b|\b172\.(1[6-9]|2\d|3[01])\.\d+\.\d+\b', line) and 'example' not in line.lower() and 'comment' not in line.lower():
|
||||
print(f' [INFO] {path}:{i}: hardcoded private IP')
|
||||
except:
|
||||
pass
|
||||
|
||||
print()
|
||||
print('=' * 60)
|
||||
print('SCAN COMPLETE')
|
||||
print('=' * 60)
|
||||
Reference in New Issue
Block a user