rugmuncher-backend / backend /app /test_audit_report_validator.py
RMI Platform
Nightly Builder v3: Liquidity Scam Detector
959432a
Raw
History Blame Contribute Delete
12.5 kB
"""
Tests for Audit Report Validator (audit_report_validator.py)
"""
import pytest
from app.audit_report_validator import (
AuditClaim,
AuditReportValidator,
AuditRisk,
SignalType,
)
@pytest.fixture
def validator() -> AuditReportValidator:
return AuditReportValidator()
@pytest.mark.asyncio
async def test_no_claims_returns_safe(validator: AuditReportValidator) -> None:
"""No audit claims = safe (no red flags)."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=None,
deploy_timestamp=1700000000,
)
assert result.risk_level == AuditRisk.SAFE
assert result.risk_score == 0.0
assert len(result.signals) == 0
@pytest.mark.asyncio
async def test_empty_claims_returns_safe(validator: AuditReportValidator) -> None:
"""Empty claims list = safe."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[],
)
assert result.risk_level == AuditRisk.SAFE
@pytest.mark.asyncio
async def test_legitimate_certik_claim_passes(validator: AuditReportValidator) -> None:
"""A legitimate-looking Certik claim should produce no high-risk signals."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Certik",
"report_url": "https://www.certik.com/projects/example-project",
"report_id": "Certik-Example-2024-001",
"report_text": "We have thoroughly reviewed the smart contract for ExampleToken. "
"Our analysis identified 3 low-risk findings related to reentrancy guards, "
"access control patterns, and integer overflow protection. All findings "
"have been addressed in the final deployment.",
}],
deploy_timestamp=1700000000,
)
# Should not flag Certik's own domain
assert not any(s.signal_type == SignalType.SUSPICIOUS_DOMAIN for s in result.signals)
# Should not flag "certik" as fake auditor name
assert not any(s.signal_type == SignalType.FAKE_AUDITOR_NAME for s in result.signals)
@pytest.mark.asyncio
async def test_fake_auditor_name_detected(validator: AuditReportValidator) -> None:
"""Misspelled auditor names are flagged."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "CertiK Pro Audit",
"report_url": "https://certik-pro-audit.xyz/report/123",
}],
)
fake_signals = [s for s in result.signals if s.signal_type == SignalType.FAKE_AUDITOR_NAME]
assert len(fake_signals) >= 1
assert result.risk_level in (AuditRisk.HIGH, AuditRisk.CRITICAL)
@pytest.mark.asyncio
async def test_suspicious_tld_flagged(validator: AuditReportValidator) -> None:
"""Audit hosted on .xyz domain should be flagged."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "SolidProof",
"report_url": "https://audit-solidproof.xyz/report.pdf",
}],
)
domain_signals = [s for s in result.signals if s.signal_type == SignalType.SUSPICIOUS_DOMAIN]
assert len(domain_signals) >= 1
assert any(".xyz" in s.detail for s in domain_signals)
@pytest.mark.asyncio
async def test_timeline_audit_before_deploy(validator: AuditReportValidator) -> None:
"""Audit dated before deployment should be flagged."""
# Deploy in 2025, audit in 2024
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Certik",
"report_date": "2024-06-01",
}],
deploy_timestamp=1735689600, # Jan 1 2025
)
timeline_signals = [s for s in result.signals if s.signal_type == SignalType.TIMELINE_ANOMALY]
assert len(timeline_signals) >= 1
@pytest.mark.asyncio
async def test_future_audit_date_flagged(validator: AuditReportValidator) -> None:
"""Audit dated in the future should be flagged."""
import time
future_ts = int(time.time()) + 86400 * 30 # 30 days in future
future_date_str = __import__("datetime").datetime.fromtimestamp(
future_ts, tz=__import__("datetime").timezone.utc
).strftime("%Y-%m-%d")
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Hacken",
"report_date": future_date_str,
}],
)
future_signals = [s for s in result.signals if s.signal_type == SignalType.TIMELINE_ANOMALY]
assert len(future_signals) >= 1
@pytest.mark.asyncio
async def test_template_language_detection(validator: AuditReportValidator) -> None:
"""Report text with many generic phrases should be flagged."""
text = (
"We have thoroughly reviewed the smart contract and no critical vulnerabilities "
"were found. The contract appears to be secure. Our team of experienced auditors "
"has completed the security audit. This confirms the safety of the contract. "
"No centralization risks found. Liquidity is locked permanently. "
"Ownership has been renounced. The code follows best practices. "
"All findings have been resolved."
)
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Some Audit Firm",
"report_text": text,
}],
)
template_signals = [
s for s in result.signals
if s.signal_type in (SignalType.TEMPLATE_REUSE, SignalType.GENERIC_LANGUAGE)
]
assert len(template_signals) >= 1
@pytest.mark.asyncio
async def test_google_drive_hosting_flagged(validator: AuditReportValidator) -> None:
"""Audit hosted on Google Drive should be flagged."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "SlowMist",
"report_url": "https://drive.google.com/file/d/abc123/view",
}],
)
domain_signals = [s for s in result.signals if s.signal_type == SignalType.SUSPICIOUS_DOMAIN]
assert len(domain_signals) >= 1
@pytest.mark.asyncio
async def test_badge_data_uri_flagged(validator: AuditReportValidator) -> None:
"""Badge using data URI should be flagged."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Certik",
"verified_badge_url": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjx0ZXh0PkNVTVNUT00gQVVESVQ8L3RleHQ+PC9zdmc+",
}],
)
forged_signals = [s for s in result.signals if s.signal_type == SignalType.FORGED_REPORT]
assert len(forged_signals) >= 1
@pytest.mark.asyncio
async def test_multiple_claims_aggregate_score(validator: AuditReportValidator) -> None:
"""Multiple suspicious claims should increase risk score."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="bsc",
claims=[
{
"auditor_name": "Certiik Pro",
"report_url": "https://certiik-pro.xyz/audit.pdf",
"report_date": "2024-01-01",
},
{
"auditor_name": "Hacken Audit Service",
"report_url": "https://hacken-audit-service.top/report/123",
"report_date": "2025-12-01",
},
],
deploy_timestamp=1735689600, # Jan 1 2025
)
# Multiple signals across claims
assert len(result.signals) >= 3
# Score should be elevated
assert result.risk_score >= 0.3
@pytest.mark.asyncio
async def test_domain_impersonation_detected(validator: AuditReportValidator) -> None:
"""URL containing auditor name but not the official domain should be flagged."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Certik",
"report_url": "https://certik-verify.xyz/audit/example",
}],
)
# Should detect this as domain impersonation
domain_signals = [s for s in result.signals if s.signal_type == SignalType.SUSPICIOUS_DOMAIN]
assert len(domain_signals) >= 1
@pytest.mark.asyncio
async def test_short_report_text_flagged(validator: AuditReportValidator) -> None:
"""Very short report text should be flagged as suspicious."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "FakeAudit",
"report_text": "This contract is safe. No issues found. Trust us.",
}],
)
generic_signals = [s for s in result.signals if s.signal_type == SignalType.GENERIC_LANGUAGE]
assert len(generic_signals) >= 1
@pytest.mark.asyncio
async def test_generic_auditor_name_flagged(validator: AuditReportValidator) -> None:
"""Generic-sounding names should be flagged."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "Secure Audit Labs Consulting",
"report_url": "https://secure-audit-labs.com/report",
}],
)
fake_signals = [s for s in result.signals if s.signal_type == SignalType.FAKE_AUDITOR_NAME]
assert len(fake_signals) >= 1
@pytest.mark.asyncio
async def test_date_parsing_various_formats(validator: AuditReportValidator) -> None:
"""Various date formats should be parsed correctly."""
# ISO format
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{"auditor_name": "Certik", "report_date": "2024-06-15T10:30:00Z"}],
deploy_timestamp=1735689600,
)
timeline_signals = [s for s in result.signals if s.signal_type == SignalType.TIMELINE_ANOMALY]
assert len(timeline_signals) >= 1
@pytest.mark.asyncio
async def test_invalid_date_format_handled(validator: AuditReportValidator) -> None:
"""Invalid date strings should not crash."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{"auditor_name": "Hacken", "report_date": "not-a-date"}],
)
# Should still work, just produce a metadata anomaly instead of crashing
assert result.error is None
@pytest.mark.asyncio
async def test_combined_fake_badge_and_url(validator: AuditReportValidator) -> None:
"""Multiple red flags on same claim compound signals."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "CeRTiK",
"report_url": "https://certik-verified-badge.top/check",
"verified_badge_url": "data:image/png;base64,FAKEBADGE",
"report_text": "This contract is safe and secure. No issues found.",
}],
)
assert len(result.signals) >= 3
assert result.risk_level in (AuditRisk.HIGH, AuditRisk.CRITICAL)
assert result.risk_score >= 0.5
@pytest.mark.asyncio
async def test_suspicious_subdomain(validator: AuditReportValidator) -> None:
"""URLs with 'verify' subdomains should raise low-severity flags."""
result = await validator.validate(
token_address="0x1234567890abcdef1234567890abcdef12345678",
chain="ethereum",
claims=[{
"auditor_name": "SolidProof",
"report_url": "https://verify.solidproof.io/audit",
}],
)
# This is a real pattern scammers use
domain_signals = [s for s in result.signals if s.signal_type == SignalType.SUSPICIOUS_DOMAIN]
assert len(domain_signals) >= 1