"""Explicit authorized external dependency policy and private native-profile exposure. Never copy third-party assets or expose the whole workshop tree to Workbench. """ from pathlib import Path, PurePosixPath import hashlib,json,re,stat BASE='58D0FB3206B6F859' DIRECT=('595F2BF2F44836FB','629B2BA37EFFD577','5E524E4FEECCA92B') ALLOWED={ '595F2BF2F44836FB':{BASE,'1337C0DE5DABBEEF','BADC0DEDABBEDA5E'}, '1337C0DE5DABBEEF':{BASE},'BADC0DEDABBEDA5E':{BASE}, '629B2BA37EFFD577':{BASE},'5E389BB9F58B79A6':{BASE,'629B2BA37EFFD577'}, '5E524E4FEECCA92B':{BASE,'5E389BB9F58B79A6','629B2BA37EFFD577'}, } def require(value,message): if not value:raise ValueError(message) def parse_project(text): guid=re.search(r'\bGUID\s+"([A-F0-9]{16})"',text) deps=re.search(r'\bDependencies\s*\{([^{}]*)\}',text) require(guid and deps,'Missing addon identity/dependencies') values=re.findall(r'"([A-F0-9]{16})"',deps[1]);require(len(values)==len(set(values)),'Duplicate dependency') return guid[1],set(values) def policy(root): value=json.loads((Path(root)/'docs/external-addons.json').read_text()) require(value.get('schema_version')==1 and set(value['addons'])==set(ALLOWED),'Unreviewed external addon policy') require(set(value['direct_ai_dependencies'])==set(DIRECT),'Unreviewed direct AI dependencies') for guid,entry in value['addons'].items(): require(set(entry['dependencies'])==ALLOWED[guid],'Unreviewed transitive dependency: '+guid) require(re.fullmatch(r'[a-f0-9]{64}',entry['project_sha256']) is not None,'Missing manifest fingerprint') for path,entry in value['resources'].items(): require(not PurePosixPath(path).is_absolute() and '..' not in PurePosixPath(path).parts and '\\' not in path,'Unsafe resource path') require(entry['addon'] in ALLOWED and re.fullmatch(r'[A-F0-9]{16}',entry['guid']) is not None,'Unknown resource owner/identity') return value def direct_for(project):return set(DIRECT) if project=='core' else set() def resource_allowed(value,visible,guid,path): entry=value['resources'].get(path) return bool(entry and entry['guid']==guid and entry['addon'] in visible) def fingerprint(path,cache): path=Path(path);before=path.stat();require(path.is_file() and not path.is_symlink(),'External input must be a regular file: '+str(path)) key=str(path);stamp=[before.st_dev,before.st_ino,before.st_size,before.st_mtime_ns,before.st_ctime_ns] old=cache.get(key) if old and old.get('stat')==stamp:return old digest=hashlib.sha256() with path.open('rb') as stream: for block in iter(lambda:stream.read(4*1024*1024),b''):digest.update(block) after=path.stat();require(stamp==[after.st_dev,after.st_ino,after.st_size,after.st_mtime_ns,after.st_ctime_ns],'External file changed while hashing') result={'stat':stamp,'sha256':digest.hexdigest()};cache[key]=result;return result def packed_input_files(folder): # Installed workshop inputs are flat packed addons. A linked folder must not # silently expose loose Scripts/Prefabs or resources outside this fingerprint. files=[] for path in sorted(Path(folder).iterdir()): require(path.is_file() and not path.is_symlink(),'External packed input must be a flat regular file: '+str(path)) name=path.name runtime=name in {'addon.gproj','resourceDatabase.rdb'} or re.fullmatch(r'[A-Za-z0-9_.-]+\.pak',name) media=name=='thumbnail.png' or re.fullmatch(r'gallery[0-9]+_[0-9]+x[0-9]+\.(?:jpg|png)',name) sidecar=re.fullmatch(r'(?:addon\.gproj|resourceDatabase\.rdb|[A-Za-z0-9_.-]+\.pak|thumbnail\.png|gallery[0-9]+_[0-9]+x[0-9]+\.(?:jpg|png))_[0-9]+(?:\.[0-9]+)*_manifest\.json',name) require(runtime or media or sidecar or name in {'meta','ServerData.json'},'Unreviewed external packed input: '+str(path)) files.append(path) require(any(path.suffix=='.pak' for path in files),'Missing runtime PAK: '+str(folder)) return files def installed(source,value,cache=None): source=Path(source).resolve();require(source.is_dir(),'External addon source directory missing') cache={} if cache is None else cache selected={} for folder in source.iterdir(): if not folder.is_dir() or folder.is_symlink():continue manifest=folder/'addon.gproj' if not manifest.is_file() or manifest.is_symlink():continue text=manifest.read_text();match=re.search(r'\bGUID\s+"([A-F0-9]{16})"',text) if not match or match[1] not in ALLOWED:continue guid=match[1] require(guid not in selected,'Duplicate installed authorized addon: '+guid) selected[guid]=folder require(set(selected)==set(ALLOWED),'Missing installed authorized external addons: '+','.join(sorted(set(ALLOWED)-set(selected)))) records={} for guid,folder in sorted(selected.items()): entry=value['addons'][guid];manifest=folder/'addon.gproj';actual,deps=parse_project(manifest.read_text()) require(actual==guid and deps==ALLOWED[guid],'Installed dependency closure changed: '+guid) require(hashlib.sha256(manifest.read_bytes()).hexdigest()==entry['project_sha256'],'Installed project manifest changed; audit update required: '+guid) database=folder/'resourceDatabase.rdb';require(database.is_file() and not database.is_symlink(),'Missing installed resource database: '+guid) data=database.read_bytes() for path,resource in value['resources'].items(): if resource['addon']!=guid:continue # Observed RDB resource record: NUL-terminated path, six-byte type header, # then little-endian 64-bit resource identity. Bind both name and exact GUID. needle=path.encode()+b'\0'+b'\x06\0\0\0\0\0'+bytes.fromhex(resource['guid'])[::-1] require(data.count(needle)==1,'Installed resource identity missing/ambiguous: '+path) files=packed_input_files(folder) records[guid]={'path':str(folder),'title':entry['title'],'version':entry['version'],'files':{p.name:fingerprint(p,cache) for p in files}} return records def prepare_profile(addons,source,value,cache_path): addons=Path(addons);cache_path=Path(cache_path) cache=json.loads(cache_path.read_text()) if cache_path.is_file() else {} records=installed(source,value,cache) names={'ERRI_'+guid for guid in ALLOWED} require(all(p.name in names|{'ArmaReforger','saves'} for p in addons.iterdir()),'Unrelated addon in private external profile') for guid,entry in records.items(): link=addons/('ERRI_'+guid);target=Path(entry['path']) if link.exists() or link.is_symlink():require(link.is_symlink() and link.resolve()==target,'Unexpected private dependency link: '+str(link)) else:link.symlink_to(target,target_is_directory=True) cache_path.write_text(json.dumps(cache,indent=2)+'\n') return records def verify_profile(records,source,value,cache_path,addons): addons=Path(addons) require(addons.is_dir() and not addons.is_symlink(),'Private external addon directory changed') expected={'ERRI_'+guid for guid in records} require(all(path.name in expected|{'ArmaReforger','saves'} for path in addons.iterdir()),'Unrelated addon in private external profile') for guid,entry in records.items(): link=addons/('ERRI_'+guid) require(link.is_symlink() and link.resolve()==Path(entry['path']),'Private external dependency link changed: '+str(link)) cache=json.loads(Path(cache_path).read_text()) return installed(source,value,cache)==records