import unittest import shutil from core.h5_kws import ( build_h5_kws_script_ticket, build_h5_kws_config_request_data, build_h5_kws_default_ticket, decrypt_h5_kws_config_response_data_rsp, run_h5_kws_sign_script, webweapon_aes_decrypt_b64, ) class H5KwsTests(unittest.TestCase): @unittest.skipIf(shutil.which("node") is None, "Node.js runner not available") def test_kws_sign_script_runner_matches_stable_callback_code(self): """复现 KWS signUrl 脚本加载后通过 kwscb 交出的本地 code。""" code = run_h5_kws_sign_script(timeout=10) self.assertEqual( code, "04dd303d63222chdfec0087d058fb1c4c5f2eb16eefgce98b2a503578e5f8433", ) def test_kws_script_ticket_uses_server_sec_token_and_script_code(self): ticket = build_h5_kws_script_ticket( sec_token="S" * 88, kwscode="04dd303d63222chdfec0087d058fb1c4c5f2eb16eefgce98b2a503578e5f8433", ) self.assertEqual( ticket, { "kwpsecproductname": "kuaishou-growth", "kwssectoken": "S" * 88, "kwscode": "04dd303d63222chdfec0087d058fb1c4c5f2eb16eefgce98b2a503578e5f8433", }, ) def test_default_ticket_matches_web_bundle_sample(self): """复现 WebWeapon `getDefaultData(true)` 的本地 fallback 票据。""" ticket = build_h5_kws_default_ticket( url=( "https://nebula.kuaishou.com/nebula/task/earning?" "layoutType=4&source=bottom_guide_first&extra=abc" ), did="ANDROID_0123456789abcdef", now_ms=1780000000123, fingerprint_nonce="AbCdEf12", sec_token="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB", ) self.assertEqual(ticket.kwpsecproductname, "kuaishou-growth") self.assertEqual(ticket.kwssectoken, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB") self.assertEqual( ticket.kwfv1, "KQKRjWEINlzEI07iMa8YrvXGWc4qQ2YMsjv4wGL2oLDzOX3k0vIz1mXqAtSlzbvkju0AoWF93jjHXryshL/TDO0VJp/Lota4HFS2Mi5xjOejJCLcEhKThUOWDhQwAGt6BG6mZ0U20xRPtGw3KZrazWgQB6/GztmjF6KxyG5emvHszKVkpm3X5J93BHDdQ3W41Co2/U4S0E86F1q0zW/cyrCwF==", ) self.assertEqual( ticket.kwscode, "K8ZTkWg+lUrqr/+G/DimeUiuHJqKK49sakhu1xkYvFmL7eXw/X0/ikDZVqiPY76dYsTargl4pE6Y6DQay4/0WM9i2PUZz6UITH0zu1YaBVY1JsgUuginAF/E2gZ7rZUcs2JtpERewBFnaT9B0kPW6o5vXmoZ48lifl/l+78Jo7McWyVS1oAr3z793qrkUv01BG0poEd2k90MUgASxjumrZHQS==", ) self.assertEqual(ticket.kww, ticket.kwfv1) self.assertEqual(len(ticket.kwfv1), 219) self.assertEqual(len(ticket.kwscode), 219) def test_default_ticket_truncates_url_like_encode_uri(self): ticket = build_h5_kws_default_ticket( url="https://nebula.kuaishou.com/a b?x=1&y=中文" + "z" * 90, did="ANDROID_0123456789abcdef", now_ms=1780000000123, fingerprint_nonce="AbCdEf12", sec_token="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB", ) self.assertIn("%20", ticket.fingerprint_plain) self.assertIn("%E4%B8%AD%E6%96%87", ticket.fingerprint_plain) encoded_url = ticket.fingerprint_plain.split("|", 1)[0] self.assertLessEqual(len("https://nebula.kuaishou.com/a b?x=1&y=中文" + "z" * 90), 160) self.assertTrue(encoded_url.startswith("https://nebula.kuaishou.com/a%20b?x=1&y=")) def test_config_request_data_matches_webweapon_aes_cbc_sample(self): request = build_h5_kws_config_request_data( did="ANDROID_0123456789abcdef", ts_ms=1780000000123, ) self.assertEqual(request.product_name, "kuaishou-growth") self.assertEqual(request.ts, 1780000000123) self.assertEqual( request.plain, ( '{"productName":"kuaishou-growth","ts":1780000000123,' '"did":"ANDROID_0123456789abcdef"}' ), ) self.assertEqual( request.data, ( "1gCA6FEVQdV1z+g5/OHPFHQhSPtfefhe159CsssADX42V2QfWZ3GbHoE/" "4/QHLX6YvQ4tHJL9iMgRQSVQNBbxgH7MfSpWk5mt8mPG4/GdzHy1QQ" "tUJOWB/vFAodCz+wX" ), ) self.assertEqual(request.body(), {"data": request.data}) def test_config_response_data_rsp_decrypts_webweapon_config(self): data_rsp = ( "In+WxaasWqdkyNTjF1LD39c5IFwB+VbXF6HHmrBh2/WEAjVtlI9JWGXRsS" "kun/BRh3XRJLkB8iq8vroBqF33wHdJd/34K3a7bavpQFBOvDBaOY9wtUQ" "bBbXs+y7JIrarlK4iD1Og6JGNZSvyPm+AwzN28v+foKtnUEA4cGTbYJw" "5g7TdUtw6h0wtdmNlCn6km9hvsoHOC6FcXBoU1lK5I3vuwAa3bqvLlYQ" "RKYwm9Cc=" ) config = decrypt_h5_kws_config_response_data_rsp(data_rsp) self.assertEqual(config["fpUrl"], "https://gdfp.gifshow.com/s/w/fp.js") self.assertEqual(config["signUrl"], "https://gdfp.gifshow.com/s/w/sign.js") self.assertEqual( config["secToken"], "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB", ) def test_webweapon_aes_decrypt_rejects_bad_padding(self): with self.assertRaises(ValueError): webweapon_aes_decrypt_b64("AAAAAAAAAAAAAAAAAAAAAA==") if __name__ == "__main__": unittest.main()