Files
mvt/tests/ios_backup/test_safari_browserstate.py
T

106 lines
3.8 KiB
Python

# Mobile Verification Toolkit (MVT)
# Copyright (c) 2021-2023 The MVT Authors.
# Use of this software is governed by the MVT License 1.1 that can be found at
# https://license.mvt.re/1.1/
import logging
import shutil
import sqlite3
import pytest
from mvt.common.indicators import Indicators
from mvt.common.module import run_module
from mvt.ios.modules.mixed.safari_browserstate import SafariBrowserState
from ..utils import add_backup_manifest_entry, get_ios_backup_folder
# fileID of HomeDomain::Library/Safari/BrowserState.db in the test backup.
DEFAULT_BROWSER_STATE_FILE_ID = "3a47b0981ed7c10f3e2800aa66bac96a3b5db28e"
PROFILE_UUID = "00000000-0000-4000-A000-000000000001"
PROFILE_BROWSER_STATE_FILE_ID = "bb00000000000000000000000000000000000001"
@pytest.fixture
def backup_with_safari_profile(tmp_path):
"""An iTunes backup where a Safari profile has its own browser state."""
backup_path = tmp_path / "backup"
shutil.copytree(get_ios_backup_folder(), backup_path)
profile_db = (
backup_path / PROFILE_BROWSER_STATE_FILE_ID[:2] / PROFILE_BROWSER_STATE_FILE_ID
)
profile_db.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(
backup_path / DEFAULT_BROWSER_STATE_FILE_ID[:2] / DEFAULT_BROWSER_STATE_FILE_ID,
profile_db,
)
add_backup_manifest_entry(
backup_path,
PROFILE_BROWSER_STATE_FILE_ID,
"AppDomain-com.apple.mobilesafari",
f"Library/Safari/Profiles/{PROFILE_UUID}/BrowserState.db",
)
return str(backup_path)
class TestSafariBrowserStateModule:
def test_parsing(self):
m = SafariBrowserState(target_path=get_ios_backup_folder())
m.is_backup = True
run_module(m)
assert len(m.results) == 1
assert len(m.timeline) == 1
assert len(m.alertstore.alerts) == 0
assert "tab" in m.results[0]
def test_parsing_backup_with_profile(self, backup_with_safari_profile):
m = SafariBrowserState(target_path=backup_with_safari_profile)
m.is_backup = True
run_module(m)
# Both the default profile and the named profile are extracted.
assert len(m.results) == 2
assert len({result["safari_browser_state_db"] for result in m.results}) == 2
def test_detection(self, indicator_file):
m = SafariBrowserState(target_path=get_ios_backup_folder())
m.is_backup = True
ind = Indicators(log=logging.getLogger())
ind.parse_stix2(indicator_file)
# Adds a file that exists in the manifest.
ind.ioc_collections[0]["domains"].append("en.wikipedia.org")
m.indicators = ind
run_module(m)
assert len(m.alertstore.alerts) == 1
assert len(m.results) == 1
assert m.results[0]["tab_url"] == "https://en.wikipedia.org/wiki/NSO_Group"
def test_tabs_without_session_rows_are_not_dropped(self, tmp_path):
db_path = tmp_path / "BrowserState.db"
conn = sqlite3.connect(db_path)
conn.executescript(
"""
CREATE TABLE tabs (
uuid TEXT, title TEXT, url TEXT, user_visible_url TEXT,
last_viewed_time REAL, private_browsing INTEGER
);
CREATE TABLE tab_sessions (tab_uuid TEXT, session_data BLOB);
INSERT INTO tabs VALUES (
'tab-1', 'Example', 'https://example.test',
'https://example.test', 700000000, 1
);
"""
)
conn.close()
module = SafariBrowserState(target_path=str(tmp_path))
module._process_browser_state_db(str(db_path))
assert len(module.results) == 1
assert module.results[0]["tab_url"] == "https://example.test"
assert module.results[0]["session_data"] == []
assert module.results[0]["tab"]["private_browsing"] == 1
assert module.results[0]["tab"]["session_data"] is None