| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- """Inventory source changes without including private data, generated releases or retired demos."""
- import hashlib
- import json
- from pathlib import Path
- import re
- import subprocess
- ROOT = Path(__file__).resolve().parents[1]
- ALLOWED = ('scripts/', 'docs/', 'deploy/', 'integrations/', 'server/', 'skills/', 'test/')
- def main():
- pending = subprocess.check_output(['git', 'ls-files', '--others', '--exclude-standard', '-z'], cwd=ROOT).decode('utf-8').split('\0')
- for args in (['git', 'diff', '--name-only', '-z'], ['git', 'diff', '--cached', '--name-only', '-z']):
- pending += subprocess.check_output(args, cwd=ROOT).decode('utf-8').split('\0')
- candidates = sorted({p for p in pending if p and (p.startswith(ALLOWED) or p in {'.gitignore', '.gitattributes', 'package.json'})})
- findings = []
- forbidden = []
- for name in candidates:
- p = ROOT / name
- if not p.exists():
- continue
- if p.is_symlink() or p.suffix.lower() in {'.zip', '.sqlite', '.db', '.pem', '.key', '.pyc', '.png'} or name.endswith('credential-binding.json'):
- forbidden.append(name)
- continue
- text = p.read_text(encoding='utf-8-sig')
- for m in re.finditer(r'''(?i)\b(master_?key|service_?token|access_?token|api_?key|client_?secret)\b\s*[:=]\s*['"]([^'"\n]{16,})['"]''', text):
- if not ('test/' in name or 'server/test/' in name or Path(name).name.startswith('test_')):
- findings.append({'path': name, 'line': text.count('\n', 0, m.start()) + 1, 'field': m[1], 'literalLength': len(m[2])})
- if re.search(r'-----BEGIN (?:RSA |OPENSSH )?PRIVATE KEY-----\s+[A-Za-z0-9+/]{40}', text):
- forbidden.append(name)
- skills = {}
- for skill in (ROOT / 'skills').iterdir():
- manifest = skill / 'FILES.sha256'
- if not manifest.is_file():
- continue
- rows = manifest.read_text(encoding='utf-8').splitlines()
- for line in rows:
- expected, relative = line.split(' ', 1)
- actual = hashlib.sha256((skill / relative).read_bytes()).hexdigest()
- if expected != actual:
- raise ValueError('SKILL_MANIFEST_MISMATCH:' + skill.name + '/' + relative)
- skills[skill.name] = {'filesVerified': len(rows), 'manifestSha256': hashlib.sha256(manifest.read_bytes()).hexdigest()}
- out = {'ok': not forbidden and not findings, 'sourceCandidates': candidates, 'sourceCandidateCount': len(candidates),
- 'forbiddenCandidates': forbidden, 'literalCredentialCandidates': findings, 'skills': skills,
- 'excludedLocalOutputs': ['artifacts/', 'data/', 'work/', 'private database files', 'credentials', 'retired demo drafts'],
- 'productionWrites': 0}
- target = ROOT / 'work/complete-release-20261106'
- target.mkdir(parents=True, exist_ok=True)
- (target / 'workspace-audit.json').write_text(json.dumps(out, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
- print(json.dumps({k:v for k,v in out.items() if k != 'sourceCandidates'}, ensure_ascii=False, indent=2))
- if __name__ == '__main__':
- main()
|