restructure of dirs, huge docs update

This commit is contained in:
2025-11-17 16:29:14 -06:00
parent 456e052389
commit cd840cb8ca
87 changed files with 2827 additions and 1094 deletions

384
app/tests/conftest.py Normal file
View File

@@ -0,0 +1,384 @@
"""
Pytest configuration and fixtures for SneakyScanner tests.
"""
import os
import tempfile
from datetime import datetime
from pathlib import Path
import pytest
import yaml
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from web.app import create_app
from web.models import Base, Scan
from web.utils.settings import PasswordManager, SettingsManager
@pytest.fixture(scope='function')
def test_db():
"""
Create a temporary test database.
Yields a SQLAlchemy session for testing, then cleans up.
"""
# Create temporary database file
db_fd, db_path = tempfile.mkstemp(suffix='.db')
# Create engine and session
engine = create_engine(f'sqlite:///{db_path}', echo=False)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
yield session
# Cleanup
session.close()
os.close(db_fd)
os.unlink(db_path)
@pytest.fixture
def sample_scan_report():
"""
Sample scan report matching the structure from scanner.py.
Returns a dictionary representing a typical scan output.
"""
return {
'title': 'Test Scan',
'scan_time': '2025-11-14T10:30:00Z',
'scan_duration': 125.5,
'config_file': '/app/configs/test.yaml',
'sites': [
{
'name': 'Test Site',
'ips': [
{
'address': '192.168.1.10',
'expected': {
'ping': True,
'tcp_ports': [22, 80, 443],
'udp_ports': [53],
'services': []
},
'actual': {
'ping': True,
'tcp_ports': [22, 80, 443, 8080],
'udp_ports': [53],
'services': [
{
'port': 22,
'service': 'ssh',
'product': 'OpenSSH',
'version': '8.9p1',
'extrainfo': 'Ubuntu',
'ostype': 'Linux'
},
{
'port': 443,
'service': 'https',
'product': 'nginx',
'version': '1.24.0',
'extrainfo': '',
'ostype': '',
'http_info': {
'protocol': 'https',
'screenshot': 'screenshots/192_168_1_10_443.png',
'certificate': {
'subject': 'CN=example.com',
'issuer': 'CN=Let\'s Encrypt Authority',
'serial_number': '123456789',
'not_valid_before': '2025-01-01T00:00:00Z',
'not_valid_after': '2025-12-31T23:59:59Z',
'days_until_expiry': 365,
'sans': ['example.com', 'www.example.com'],
'is_self_signed': False,
'tls_versions': {
'TLS 1.2': {
'supported': True,
'cipher_suites': [
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
]
},
'TLS 1.3': {
'supported': True,
'cipher_suites': [
'TLS_AES_256_GCM_SHA384',
'TLS_AES_128_GCM_SHA256'
]
}
}
}
}
},
{
'port': 80,
'service': 'http',
'product': 'nginx',
'version': '1.24.0',
'extrainfo': '',
'ostype': '',
'http_info': {
'protocol': 'http',
'screenshot': 'screenshots/192_168_1_10_80.png'
}
},
{
'port': 8080,
'service': 'http',
'product': 'Jetty',
'version': '9.4.48',
'extrainfo': '',
'ostype': ''
}
]
}
}
]
}
]
}
@pytest.fixture
def sample_config_file(tmp_path):
"""
Create a sample YAML config file for testing.
Args:
tmp_path: pytest temporary directory fixture
Returns:
Path to created config file
"""
config_data = {
'title': 'Test Scan',
'sites': [
{
'name': 'Test Site',
'ips': [
{
'address': '192.168.1.10',
'expected': {
'ping': True,
'tcp_ports': [22, 80, 443],
'udp_ports': [53],
'services': ['ssh', 'http', 'https']
}
}
]
}
]
}
config_file = tmp_path / 'test_config.yaml'
with open(config_file, 'w') as f:
yaml.dump(config_data, f)
return str(config_file)
@pytest.fixture
def sample_invalid_config_file(tmp_path):
"""
Create an invalid config file for testing validation.
Returns:
Path to invalid config file
"""
config_file = tmp_path / 'invalid_config.yaml'
with open(config_file, 'w') as f:
f.write("invalid: yaml: content: [missing closing bracket")
return str(config_file)
@pytest.fixture(scope='function')
def app():
"""
Create Flask application for testing.
Returns:
Configured Flask app instance with test database
"""
# Create temporary database
db_fd, db_path = tempfile.mkstemp(suffix='.db')
# Create app with test config
test_config = {
'TESTING': True,
'SQLALCHEMY_DATABASE_URI': f'sqlite:///{db_path}',
'SECRET_KEY': 'test-secret-key'
}
app = create_app(test_config)
yield app
# Cleanup
os.close(db_fd)
os.unlink(db_path)
@pytest.fixture(scope='function')
def client(app):
"""
Create Flask test client.
Args:
app: Flask application fixture
Returns:
Flask test client for making API requests
"""
return app.test_client()
@pytest.fixture(scope='function')
def db(app):
"""
Alias for database session that works with Flask app context.
Args:
app: Flask application fixture
Returns:
SQLAlchemy session
"""
with app.app_context():
yield app.db_session
@pytest.fixture
def sample_scan(db):
"""
Create a sample scan in the database for testing.
Args:
db: Database session fixture
Returns:
Scan model instance
"""
scan = Scan(
timestamp=datetime.utcnow(),
status='completed',
config_file='/app/configs/test.yaml',
title='Test Scan',
duration=125.5,
triggered_by='test',
json_path='/app/output/scan_report_20251114_103000.json',
html_path='/app/output/scan_report_20251114_103000.html',
zip_path='/app/output/scan_report_20251114_103000.zip',
screenshot_dir='/app/output/scan_report_20251114_103000_screenshots'
)
db.add(scan)
db.commit()
db.refresh(scan)
return scan
# Authentication Fixtures
@pytest.fixture
def app_password():
"""
Test password for authentication tests.
Returns:
Test password string
"""
return 'testpassword123'
@pytest.fixture
def db_with_password(db, app_password):
"""
Database session with application password set.
Args:
db: Database session fixture
app_password: Test password fixture
Returns:
Database session with password configured
"""
settings_manager = SettingsManager(db)
PasswordManager.set_app_password(settings_manager, app_password)
return db
@pytest.fixture
def db_no_password(app):
"""
Database session without application password set.
Args:
app: Flask application fixture
Returns:
Database session without password
"""
with app.app_context():
# Clear any password that might be set
settings_manager = SettingsManager(app.db_session)
settings_manager.delete('app_password')
yield app.db_session
@pytest.fixture
def authenticated_client(client, db_with_password, app_password):
"""
Flask test client with authenticated session.
Args:
client: Flask test client fixture
db_with_password: Database with password set
app_password: Test password fixture
Returns:
Test client with active session
"""
# Log in
client.post('/auth/login', data={
'password': app_password
})
return client
@pytest.fixture
def client_no_password(app):
"""
Flask test client with no password set (for setup testing).
Args:
app: Flask application fixture
Returns:
Test client for testing setup flow
"""
# Create temporary database without password
db_fd, db_path = tempfile.mkstemp(suffix='.db')
test_config = {
'TESTING': True,
'SQLALCHEMY_DATABASE_URI': f'sqlite:///{db_path}',
'SECRET_KEY': 'test-secret-key'
}
test_app = create_app(test_config)
test_client = test_app.test_client()
yield test_client
# Cleanup
os.close(db_fd)
os.unlink(db_path)