Initial server source import
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
"""Pure translation of the reviewed Windows config; YAML and file copies are caller-owned."""
|
||||
|
||||
from copy import deepcopy
|
||||
import ntpath
|
||||
|
||||
|
||||
# Only these reviewed absolute Windows paths have known container replacements.
|
||||
_FIXED_GLOBAL_PATHS = {
|
||||
'root_dir': (r'D:\truf', '/opt/truf'),
|
||||
'project_dir': (r'D:\truf\app', '/opt/truf/app'),
|
||||
'runtime_dir': (r'D:\truf\runtime', '/data/runtime-linux'),
|
||||
'postgres_data_dir': (r'S:\postgres-data', '/data/postgres-linux'),
|
||||
'postgres_bin_dir': (r'D:\truf\runtime\postgres\pgsql\bin', '/usr/lib/postgresql/16/bin'),
|
||||
'result_bundle_dir': (r'S:\scanner-result-bundles', '/data/scanner-result-bundles'),
|
||||
'work_dir': (r'S:\scanner-work', '/data/scanner-work'),
|
||||
'control_dir': (r'D:\truf\runtime\control', '/run/truf/control'),
|
||||
'trufflehog_path': (r'C:\Tools\trufflehog.exe', '/usr/local/bin/trufflehog'),
|
||||
'proxy_file': (r'D:\truf\runtime\proxy.txt', '/data/runtime-linux/proxy.txt'),
|
||||
'secrets_file': (r'D:\truf\app\secrets.yaml', '/data/config/secrets.yaml'),
|
||||
'trufflehog_config': (
|
||||
r'D:\truf\app\trufflehog-custom-detectors.yaml',
|
||||
'/data/config/trufflehog-custom-detectors.yaml',
|
||||
),
|
||||
}
|
||||
_PATH_FIELDS = {
|
||||
'global': (
|
||||
'result_spool_dir', 'legacy_result_spool_dir', 'results_dir', 'queue_dir',
|
||||
'state_dir', 'log_dir', 'keycheck_dir', 'postman_cache_dir', 'gharchive_cache_dir',
|
||||
'database_path', 'state_file', 'api_proxy_file', 'download_proxy_file',
|
||||
'dashboard_db_path', 'scan_limiter_db', 'dockerhub_tag_cache_path',
|
||||
),
|
||||
'supervisor': ('log_dir', 'supervisor_log', 'status_file', 'dashboard_log', 'state_dir'),
|
||||
'keychecks': ('input', 'proxy_file', 'keycheck_dir', 'summary_tsv', 'summary_json', 'alive_summary_tsv'),
|
||||
}
|
||||
_SOURCE_PATH_FIELDS = ('target_file', 'trufflehog_config', 'postman_cache_dir', 'gharchive_cache_dir')
|
||||
_WINDOWS_KNOBS = (
|
||||
'trufflehog_job_memory_limit_bytes',
|
||||
'trufflehog_windows_job_cpu_weight',
|
||||
'trufflehog_windows_memory_priority',
|
||||
)
|
||||
|
||||
|
||||
def translate_windows_config(original, baseline):
|
||||
"""Return an independent config and sorted, changed dotted key paths (never values).
|
||||
|
||||
``baseline`` is the parsed config.linux.yaml, not a general merge source.
|
||||
Fixed paths must match the container contract. Other known path fields keep
|
||||
relative paths/templates with Linux separators; unreviewed Windows absolute
|
||||
paths raise ValueError naming only the key. No environment, filesystem,
|
||||
runtime, database, or YAML operations are performed.
|
||||
"""
|
||||
if not isinstance(original, dict) or not isinstance(baseline, dict):
|
||||
raise TypeError('original and baseline must be dictionaries')
|
||||
config = deepcopy(original)
|
||||
adjusted = set()
|
||||
|
||||
def assign(mapping, key, value, prefix):
|
||||
if key not in mapping or mapping[key] != value:
|
||||
mapping[key] = deepcopy(value)
|
||||
adjusted.add(prefix + '.' + key)
|
||||
|
||||
def path_value(value, key_path, approved=None):
|
||||
if value is None:
|
||||
return value
|
||||
if not isinstance(value, str):
|
||||
raise ValueError('Expected path string at ' + key_path)
|
||||
if ntpath.splitdrive(value)[0] or value.startswith('\\'):
|
||||
if approved is None or ntpath.normcase(ntpath.normpath(value)) != ntpath.normcase(ntpath.normpath(approved[0])):
|
||||
raise ValueError('Unsupported Windows path at ' + key_path)
|
||||
return approved[1]
|
||||
return value.replace('\\', '/')
|
||||
|
||||
linux_paths = {key: pair[1] for key, pair in _FIXED_GLOBAL_PATHS.items()}
|
||||
control = _FIXED_GLOBAL_PATHS['control_dir']
|
||||
supervisor_paths = {'control_dir': control}
|
||||
for key, filename in (('instance_file', 'supervisor.instance.json'), ('lock_file', 'supervisor.lock')):
|
||||
supervisor_paths[key] = (control[0] + '\\' + filename, control[1] + '/' + filename)
|
||||
|
||||
for section, fixed_paths in (('global', _FIXED_GLOBAL_PATHS), ('supervisor', supervisor_paths)):
|
||||
mapping = config.setdefault(section, {})
|
||||
for key, pair in fixed_paths.items():
|
||||
key_path = section + '.' + key
|
||||
value = path_value(mapping.get(key), key_path, pair)
|
||||
if key == 'trufflehog_path' and value not in (None, '', 'trufflehog', 'trufflehog.exe', pair[1]):
|
||||
raise ValueError('Unsupported executable path at ' + key_path)
|
||||
# The copied original policy intentionally replaces the image policy.
|
||||
if key != 'trufflehog_config':
|
||||
try:
|
||||
baseline_path = baseline[section][key].format_map(linux_paths)
|
||||
except (KeyError, AttributeError, ValueError):
|
||||
raise ValueError('Invalid Linux baseline path at ' + key_path) from None
|
||||
if baseline_path != pair[1]:
|
||||
raise ValueError('Invalid Linux baseline path at ' + key_path)
|
||||
assign(mapping, key, pair[1], section)
|
||||
|
||||
groups = [(section, config.get(section, {}), fields) for section, fields in _PATH_FIELDS.items()]
|
||||
groups.extend(('sources.' + name, source, _SOURCE_PATH_FIELDS)
|
||||
for name, source in config.get('sources', {}).items())
|
||||
for prefix, mapping, fields in groups:
|
||||
for key in fields:
|
||||
if key not in mapping:
|
||||
continue
|
||||
approved = None
|
||||
if key in ('proxy_file', 'api_proxy_file', 'download_proxy_file'):
|
||||
approved = _FIXED_GLOBAL_PATHS['proxy_file']
|
||||
elif key == 'trufflehog_config':
|
||||
approved = _FIXED_GLOBAL_PATHS[key]
|
||||
value = path_value(mapping[key], prefix + '.' + key, approved)
|
||||
if key == 'trufflehog_config' and value in (
|
||||
'{project_dir}/trufflehog-custom-detectors.yaml', 'trufflehog-custom-detectors.yaml',
|
||||
):
|
||||
value = linux_paths[key]
|
||||
assign(mapping, key, value, prefix)
|
||||
|
||||
for key in ('max_active_scans', 'opportunistic_scan_slots') + _WINDOWS_KNOBS:
|
||||
assign(config['global'], key, baseline['global'][key], 'global')
|
||||
for key in ('interactive', 'autostart', 'control_host', 'control_port'):
|
||||
assign(config['supervisor'], key, baseline['supervisor'][key], 'supervisor')
|
||||
assign(config['supervisor'].setdefault('dashboard', {}), 'enabled',
|
||||
baseline['supervisor']['dashboard']['enabled'], 'supervisor.dashboard')
|
||||
for name, source in config.get('sources', {}).items():
|
||||
source_baseline = baseline.get('sources', {}).get(name, {})
|
||||
for key in _WINDOWS_KNOBS:
|
||||
if key in source or key in source_baseline:
|
||||
assign(source, key, source_baseline.get(key, baseline['global'][key]), 'sources.' + name)
|
||||
|
||||
return config, sorted(adjusted)
|
||||
Reference in New Issue
Block a user