Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions admin/scripts/check_html_safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,6 @@

# Reviewed exceptions expected to stay. Every entry needs a comment saying why.
ALLOWLIST = {
# Declares a single media column and nothing else. The cell is built by the
# framework's media helper, so the module has no evidence text of its own to
# escape.
('scripts/artifacts/nsVault.py', 'unguarded-html-columns', '<module>'),

# Both join values produced by BeReal's own generic_url(), which returns
# safe_url() output -- escaped text, never an anchor. The values reaching the
# cell are already escaped; this check does not follow a value through a
Expand Down
78 changes: 48 additions & 30 deletions scripts/artifacts/nsVault.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
# pylint: disable=W0311
__artifacts_v2__ = {
"calculatorVault": {
"name": "Calculator Vault Application",
"description": "Parses data from the Calculator# Vault application",
"author": "@charpy4n6",
"creation_date": "2025-01-22",
"last_update_date": "2026-07-31",
"last_update_date": "2026-09-19",
"requirements": "none",
"category": "Calculator#",
"notes": "App identity is inferred from the FolderLockAdvanced.sqlite filename observed in testing; the path glob is not bundle-specific.",
"notes": "App identity is inferred from the FolderLockAdvanced.sqlite filename observed in testing; the path glob is not bundle-specific."
" On hexordia_ios1651 the database sits in the data container of net.newsoftwares.NSVault, the app named 'Calculator #'"
" (version 3.3.6), and its ZVIDEO table holds no rows."
" Attachment is the file in the same app container's Documents/FolderLockAdvanced/Videos/Movies folder whose"
" name equals the row's Video Name, and is blank when no such file was extracted. No tested image held a video"
" row, so this link has only been exercised on constructed data.",
"paths": ('*/mobile/Containers/Data/Application/*/Library/FolderLockAdvanced.sqlite*', '*/mobile/Containers/Data/Application/*/Documents/FolderLockAdvanced/Videos/Movies/*',),
"output_types": "standard",
"html_columns": ['Attachment'],
"artifact_icon": "eye-off"
"artifact_icon": "eye-off",
"sample_data": {
"hexordia_ios1651": "iOS 16.5.1 | 0 rows",
}
}
}

from scripts.ilapfuncs import artifact_processor, get_file_path, get_sqlite_db_records, convert_cocoa_core_data_ts_to_utc, media_to_html
import os
from scripts.ilapfuncs import artifact_processor, check_in_media, get_file_path, get_sqlite_db_records, convert_cocoa_core_data_ts_to_utc

@artifact_processor
def calculatorVault(context):
source_path = get_file_path(context.get_files_found(), "FolderLockAdvanced.sqlite")
files_found = context.get_files_found()
source_path = get_file_path(files_found, "FolderLockAdvanced.sqlite")
data_list = []
data_list_html = []

query = '''
SELECT
Expand All @@ -40,28 +47,39 @@ def calculatorVault(context):
'''

data_headers = (
('Modified Date', 'datetime'),
'Album Title',
'Album ID',
'Storage Path',
'Storage Thumbnail',
'Video ID',
'Video Name',
'Attachment',
'Duration',
'Video Size')

('Modified Date', 'datetime'),
'Album Title',
'Album ID',
'Storage Path',
'Storage Thumbnail',
'Video ID',
'Video Name',
('Attachment', 'media'),
'Duration',
'Video Size')

# The videos sit in the database's own app container, so only that container's
# Movies folder is searched; a file of the same name in another container is not
# this row's video.
movies = {}
if source_path:
container = os.path.dirname(os.path.dirname(source_path))
movies_dir = os.path.normpath(os.path.join(container, 'Documents', 'FolderLockAdvanced', 'Videos', 'Movies'))
for file_found in files_found:
file_found = str(file_found)
if os.path.normpath(os.path.dirname(file_found)) == movies_dir and os.path.isfile(file_found):
movies[os.path.basename(file_found)] = file_found

db_records = get_sqlite_db_records(source_path, query)

for record in db_records:
modified_date = convert_cocoa_core_data_ts_to_utc(record[0])
attachmentName = str(record[6])
thumb = media_to_html(attachmentName, context.get_files_found(), context.get_report_folder())
data_list.append(
(modified_date, record[1], record[2], record[3], record[4],
record[5], record[6], '', record[7], record[8]))
data_list_html.append(
(modified_date, record[1], record[2], record[3], record[4],
record[5], record[6], thumb, record[7], record[8]))

return data_headers, (data_list, data_list_html), source_path
modified_date = convert_cocoa_core_data_ts_to_utc(record[0])
attachment = ''
stored = movies.get(record[6])
if stored:
attachment = check_in_media(stored, os.path.basename(stored)) or ''
data_list.append(
(modified_date, record[1], record[2], record[3], record[4],
record[5], record[6], attachment, record[7], record[8]))

return data_headers, data_list, source_path
Loading