"""Synthetic, loopback-only receiver rehearsal. Never calls the site's /deploy.""" import hashlib,hmac,json,os,socket,subprocess,tempfile,time,unittest,urllib.request,urllib.error from pathlib import Path class ReceiverTests(unittest.TestCase): def test_signature_scope_replay_and_no_command_dispatch(self): source=Path(__file__).with_name('webhook.php') with tempfile.TemporaryDirectory(prefix='investmoz-webhook-QA-') as directory: root=Path(directory);state=root/'state';(state/'requests').mkdir(parents=True);(state/'incoming').mkdir() secret=root/'synthetic-secret';secret.write_text('SYNTHETIC-LOCAL-QA-ONLY') config=root/'config.json';config.write_text(json.dumps({'webhook_secret_file':str(secret),'repository':'nala/Investidor-V2','branch':'homologacao','state_root':str(state)})) env={**os.environ,'INVESTMOZ_DELIVERY_CONFIG':str(config)} with socket.socket() as reserve:reserve.bind(('127.0.0.1',0));port=reserve.getsockname()[1] with (root/'php.log').open('wb') as log: proc=subprocess.Popen(['php8.4','-S','127.0.0.1:'+str(port),'-t',str(source.parent)],env=env,stdout=log,stderr=log) try: for _ in range(60): if proc.poll() is not None:self.fail('Isolated PHP did not start') with socket.socket() as probe: if probe.connect_ex(('127.0.0.1',port))==0:break time.sleep(0.05) def post(payload,valid=True,event='push'): data=json.dumps(payload).encode();signature=hmac.new(secret.read_bytes(),data,hashlib.sha256).hexdigest() if valid else '0'*64 req=urllib.request.Request('http://127.0.0.1:'+str(port)+'/webhook.php',data=data,headers={'Content-Type':'application/json','X-Gitea-Event':event,'X-Gitea-Signature':signature}) try:res=urllib.request.urlopen(req,timeout=5) except urllib.error.HTTPError as error:res=error return res.status valid={'repository':{'full_name':'nala/Investidor-V2'},'ref':'refs/heads/homologacao','after':'a'*40} self.assertEqual(post(valid,False),403) self.assertEqual(post({**valid,'ref':'refs/heads/main'}),204) self.assertEqual(post({**valid,'repository':{'full_name':'someone/other'}}),204) self.assertEqual(post({**valid,'after':'a; touch /tmp/not-a-command'}),400) self.assertEqual(list((state/'requests').iterdir()),[]) self.assertEqual(post(valid),202);self.assertEqual(post(valid),202) files=list((state/'requests').glob('*.json'));self.assertEqual(len(files),1) self.assertEqual(json.loads(files[0].read_text())['commit'],'a'*40) self.assertEqual(list((state/'incoming').iterdir()),[]) finally: proc.terminate();proc.wait(timeout=5) if __name__=='__main__':unittest.main()