CREATIVEWITY / SCRIPT SOURCEPython 3.9+ / standard library / MIT

Readable source

#!/usr/bin/env python3
"""Report SVG dimensions and active/external content. Does not sanitise or modify SVGs."""
import argparse,json,xml.etree.ElementTree as ET
from pathlib import Path
def audit(path):
    result={'file':str(path),'issues':[]}
    try:
        if path.stat().st_size>5*1024*1024:raise ValueError('File exceeds 5 MB audit limit.')
        raw=path.read_text(encoding='utf-8')
        if '<!doctype' in raw.lower() or '<!entity' in raw.lower():raise ValueError('DOCTYPE / ENTITY declarations are not accepted.')
        root=ET.fromstring(raw)
        if root.tag.split('}')[-1]!='svg':raise ValueError('Root element is not SVG.')
        result.update(width=root.get('width'),height=root.get('height'),viewBox=root.get('viewBox'),elements=sum(1 for _ in root.iter()))
        if not root.get('viewBox'):result['issues'].append('Missing viewBox.')
        for el in root.iter():
            name=el.tag.split('}')[-1]
            if name in ('script','foreignObject'):result['issues'].append('Active content: '+name)
            for k,v in el.attrib.items():
                attr=k.split('}')[-1].lower()
                if attr.startswith('on'):result['issues'].append('Event attribute: '+attr)
                if attr=='href' and not v.startswith('#'):result['issues'].append('Non-local reference: '+v[:100])
                if any(s in v.lower() for s in ('javascript:','url(http','url(//','@import')):result['issues'].append('Active or external attribute content.')
            if name=='style' and el.text and any(s in el.text.lower() for s in ('@import','http:','https:','javascript:')):result['issues'].append('External / active style content.')
    except (ValueError,OSError,UnicodeError,ET.ParseError) as e:result['issues'].append(str(e))
    return result
def main():
    p=argparse.ArgumentParser(description=__doc__);p.add_argument('path',type=Path);a=p.parse_args();files=sorted(a.path.rglob('*.svg')) if a.path.is_dir() else [a.path]
    results=[audit(f) for f in files];print(json.dumps(results,indent=2));return 1 if any(r['issues'] for r in results) else 0
if __name__=='__main__':raise SystemExit(main())