@cyberstrike-io/cyberstrike 1.1.17-beta.2 → 1.1.17-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/hackbrowser-worker.js +501 -249
  2. package/package.json +12 -12
  3. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-10_concurrent-session-control/SKILL.md +1 -1
  4. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-11(1)_pattern-hiding-displays/SKILL.md +1 -1
  5. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-11_device-lock/SKILL.md +1 -1
  6. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-12(1)_user-initiated-logouts/SKILL.md +1 -1
  7. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-12(2)_termination-message/SKILL.md +1 -1
  8. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-12(3)_timeout-warning-message/SKILL.md +1 -1
  9. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-12_session-termination/SKILL.md +1 -1
  10. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-13_supervision-and-review-access-control/SKILL.md +1 -1
  11. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-14(1)_necessary-uses/SKILL.md +1 -1
  12. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-14_permitted-actions-without-identification-or-authentication/SKILL.md +1 -1
  13. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-15_automated-marking/SKILL.md +1 -1
  14. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(1)_dynamic-attribute-association/SKILL.md +1 -1
  15. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(10)_attribute-configuration-by-authorized-individuals/SKILL.md +1 -1
  16. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(2)_attribute-value-changes-by-authorized-individuals/SKILL.md +1 -1
  17. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(3)_maintenance-of-attribute-associations-by-system/SKILL.md +1 -1
  18. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(4)_association-of-attributes-by-authorized-individuals/SKILL.md +1 -1
  19. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(5)_attribute-displays-on-objects-to-be-output/SKILL.md +1 -1
  20. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(6)_maintenance-of-attribute-association/SKILL.md +1 -1
  21. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(7)_consistent-attribute-interpretation/SKILL.md +1 -1
  22. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(8)_association-techniques-and-technologies/SKILL.md +1 -1
  23. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16(9)_attribute-reassignment-regrading-mechanisms/SKILL.md +1 -1
  24. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-16_security-and-privacy-attributes/SKILL.md +1 -1
  25. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(1)_monitoring-and-control/SKILL.md +1 -1
  26. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(10)_authenticate-remote-commands/SKILL.md +1 -1
  27. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(2)_protection-of-confidentiality-and-integrity-using-encryption/SKILL.md +1 -1
  28. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(3)_managed-access-control-points/SKILL.md +1 -1
  29. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(4)_privileged-commands-and-access/SKILL.md +1 -1
  30. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(5)_monitoring-for-unauthorized-connections/SKILL.md +1 -1
  31. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(6)_protection-of-mechanism-information/SKILL.md +1 -1
  32. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(7)_additional-protection-for-security-function-access/SKILL.md +1 -1
  33. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(8)_disable-nonsecure-network-protocols/SKILL.md +1 -1
  34. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17(9)_disconnect-or-disable-access/SKILL.md +1 -1
  35. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-17_remote-access/SKILL.md +1 -1
  36. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-18(1)_authentication-and-encryption/SKILL.md +1 -1
  37. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-18(2)_monitoring-unauthorized-connections/SKILL.md +1 -1
  38. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-18(3)_disable-wireless-networking/SKILL.md +1 -1
  39. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-18(4)_restrict-configurations-by-users/SKILL.md +1 -1
  40. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-18(5)_antennas-and-transmission-power-levels/SKILL.md +1 -1
  41. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-18_wireless-access/SKILL.md +1 -1
  42. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-19(1)_use-of-writable-and-portable-storage-devices/SKILL.md +1 -1
  43. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-19(2)_use-of-personally-owned-portable-storage-devices/SKILL.md +1 -1
  44. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-19(3)_use-of-portable-storage-devices-with-no-identifiable-owner/SKILL.md +1 -1
  45. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-19(4)_restrictions-for-classified-information/SKILL.md +1 -1
  46. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-19(5)_full-device-or-container-based-encryption/SKILL.md +1 -1
  47. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-19_access-control-for-mobile-devices/SKILL.md +1 -1
  48. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-1_policy-and-procedures/SKILL.md +1 -1
  49. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(1)_automated-system-account-management/SKILL.md +1 -1
  50. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(10)_shared-and-group-account-credential-change/SKILL.md +1 -1
  51. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(11)_usage-conditions/SKILL.md +1 -1
  52. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(12)_account-monitoring-for-atypical-usage/SKILL.md +1 -1
  53. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(13)_disable-accounts-for-high-risk-individuals/SKILL.md +1 -1
  54. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(2)_automated-temporary-and-emergency-account-management/SKILL.md +1 -1
  55. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(3)_disable-accounts/SKILL.md +1 -1
  56. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(4)_automated-audit-actions/SKILL.md +1 -1
  57. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(5)_inactivity-logout/SKILL.md +1 -1
  58. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(6)_dynamic-privilege-management/SKILL.md +1 -1
  59. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(7)_privileged-user-accounts/SKILL.md +1 -1
  60. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(8)_dynamic-account-management/SKILL.md +1 -1
  61. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2(9)_restrictions-on-use-of-shared-and-group-accounts/SKILL.md +1 -1
  62. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-20(1)_limits-on-authorized-use/SKILL.md +1 -1
  63. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-20(2)_portable-storage-devices-restricted-use/SKILL.md +1 -1
  64. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-20(3)_non-organizationally-owned-systems-restricted-use/SKILL.md +1 -1
  65. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-20(4)_network-accessible-storage-devices-prohibited-use/SKILL.md +1 -1
  66. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-20(5)_portable-storage-devices-prohibited-use/SKILL.md +1 -1
  67. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-20_use-of-external-systems/SKILL.md +1 -1
  68. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-21(1)_automated-decision-support/SKILL.md +1 -1
  69. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-21(2)_information-search-and-retrieval/SKILL.md +1 -1
  70. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-21_information-sharing/SKILL.md +1 -1
  71. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-22_publicly-accessible-content/SKILL.md +1 -1
  72. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-23_data-mining-protection/SKILL.md +1 -1
  73. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-24(1)_transmit-access-authorization-information/SKILL.md +1 -1
  74. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-24(2)_no-user-or-process-identity/SKILL.md +1 -1
  75. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-24_access-control-decisions/SKILL.md +1 -1
  76. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-25_reference-monitor/SKILL.md +1 -1
  77. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-2_account-management/SKILL.md +1 -1
  78. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(1)_restricted-access-to-privileged-functions/SKILL.md +1 -1
  79. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(10)_audited-override-of-access-control-mechanisms/SKILL.md +1 -1
  80. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(11)_restrict-access-to-specific-information-types/SKILL.md +1 -1
  81. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(12)_assert-and-enforce-application-access/SKILL.md +1 -1
  82. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(13)_attribute-based-access-control/SKILL.md +1 -1
  83. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(14)_individual-access/SKILL.md +1 -1
  84. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(15)_discretionary-and-mandatory-access-control/SKILL.md +1 -1
  85. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(2)_dual-authorization/SKILL.md +1 -1
  86. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(3)_mandatory-access-control/SKILL.md +1 -1
  87. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(4)_discretionary-access-control/SKILL.md +1 -1
  88. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(5)_security-relevant-information/SKILL.md +1 -1
  89. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(6)_protection-of-user-and-system-information/SKILL.md +1 -1
  90. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(7)_role-based-access-control/SKILL.md +1 -1
  91. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(8)_revocation-of-access-authorizations/SKILL.md +1 -1
  92. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3(9)_controlled-release/SKILL.md +1 -1
  93. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-3_access-enforcement/SKILL.md +1 -1
  94. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(1)_object-security-and-privacy-attributes/SKILL.md +1 -1
  95. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(10)_enable-and-disable-security-or-privacy-policy-filters/SKILL.md +1 -1
  96. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(11)_configuration-of-security-or-privacy-policy-filters/SKILL.md +1 -1
  97. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(12)_data-type-identifiers/SKILL.md +1 -1
  98. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(13)_decomposition-into-policy-relevant-subcomponents/SKILL.md +1 -1
  99. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(14)_security-or-privacy-policy-filter-constraints/SKILL.md +1 -1
  100. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(15)_detection-of-unsanctioned-information/SKILL.md +1 -1
  101. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(16)_information-transfers-on-interconnected-systems/SKILL.md +1 -1
  102. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(17)_domain-authentication/SKILL.md +1 -1
  103. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(18)_security-attribute-binding/SKILL.md +1 -1
  104. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(19)_validation-of-metadata/SKILL.md +1 -1
  105. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(2)_processing-domains/SKILL.md +1 -1
  106. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(20)_approved-solutions/SKILL.md +1 -1
  107. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(21)_physical-or-logical-separation-of-information-flows/SKILL.md +1 -1
  108. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(22)_access-only/SKILL.md +1 -1
  109. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(23)_modify-non-releasable-information/SKILL.md +1 -1
  110. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(24)_internal-normalized-format/SKILL.md +1 -1
  111. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(25)_data-sanitization/SKILL.md +1 -1
  112. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(26)_audit-filtering-actions/SKILL.md +1 -1
  113. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(27)_redundantindependent-filtering-mechanisms/SKILL.md +1 -1
  114. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(28)_linear-filter-pipelines/SKILL.md +1 -1
  115. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(29)_filter-orchestration-engines/SKILL.md +1 -1
  116. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(3)_dynamic-information-flow-control/SKILL.md +1 -1
  117. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(30)_filter-mechanisms-using-multiple-processes/SKILL.md +1 -1
  118. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(31)_failed-content-transfer-prevention/SKILL.md +1 -1
  119. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(32)_process-requirements-for-information-transfer/SKILL.md +1 -1
  120. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(4)_flow-control-of-encrypted-information/SKILL.md +1 -1
  121. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(5)_embedded-data-types/SKILL.md +1 -1
  122. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(6)_metadata/SKILL.md +1 -1
  123. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(7)_one-way-flow-mechanisms/SKILL.md +1 -1
  124. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(8)_security-and-privacy-policy-filters/SKILL.md +1 -1
  125. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4(9)_human-reviews/SKILL.md +1 -1
  126. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-4_information-flow-enforcement/SKILL.md +1 -1
  127. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-5_separation-of-duties/SKILL.md +1 -1
  128. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(1)_authorize-access-to-security-functions/SKILL.md +1 -1
  129. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(10)_prohibit-non-privileged-users-from-executing-privileged-func/SKILL.md +1 -1
  130. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(2)_non-privileged-access-for-nonsecurity-functions/SKILL.md +1 -1
  131. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(3)_network-access-to-privileged-commands/SKILL.md +1 -1
  132. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(4)_separate-processing-domains/SKILL.md +1 -1
  133. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(5)_privileged-accounts/SKILL.md +1 -1
  134. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(6)_privileged-access-by-non-organizational-users/SKILL.md +1 -1
  135. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(7)_review-of-user-privileges/SKILL.md +1 -1
  136. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(8)_privilege-levels-for-code-execution/SKILL.md +1 -1
  137. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6(9)_log-use-of-privileged-functions/SKILL.md +1 -1
  138. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-6_least-privilege/SKILL.md +1 -1
  139. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-7(1)_automatic-account-lock/SKILL.md +1 -1
  140. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-7(2)_purge-or-wipe-mobile-device/SKILL.md +1 -1
  141. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-7(3)_biometric-attempt-limiting/SKILL.md +1 -1
  142. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-7(4)_use-of-alternate-authentication-factor/SKILL.md +1 -1
  143. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-7_unsuccessful-logon-attempts/SKILL.md +1 -1
  144. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-8_system-use-notification/SKILL.md +1 -1
  145. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-9(1)_unsuccessful-logons/SKILL.md +1 -1
  146. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-9(2)_successful-and-unsuccessful-logons/SKILL.md +1 -1
  147. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-9(3)_notification-of-account-changes/SKILL.md +1 -1
  148. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-9(4)_additional-logon-information/SKILL.md +1 -1
  149. package/skill/NIST/SP800-53_rev5/AC_access-control/AC-9_previous-logon-notification/SKILL.md +1 -1
  150. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-10_adaptive-authentication/SKILL.md +1 -1
  151. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-11_re-authentication/SKILL.md +1 -1
  152. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12(1)_supervisor-authorization/SKILL.md +1 -1
  153. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12(2)_identity-evidence/SKILL.md +1 -1
  154. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12(3)_identity-evidence-validation-and-verification/SKILL.md +1 -1
  155. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12(4)_in-person-validation-and-verification/SKILL.md +1 -1
  156. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12(5)_address-confirmation/SKILL.md +1 -1
  157. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12(6)_accept-externally-proofed-identities/SKILL.md +1 -1
  158. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-12_identity-proofing/SKILL.md +1 -1
  159. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-13(1)_protection-of-cryptographic-keys/SKILL.md +1 -1
  160. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-13(2)_verification-of-identity-assertions-and-access-tokens/SKILL.md +1 -1
  161. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-13(3)_token-management/SKILL.md +1 -1
  162. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-13_identity-providers-and-authorization-servers/SKILL.md +1 -1
  163. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-1_policy-and-procedures/SKILL.md +1 -1
  164. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(1)_multi-factor-authentication-to-privileged-accounts/SKILL.md +1 -1
  165. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(10)_single-sign-on/SKILL.md +1 -1
  166. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(11)_remote-access-separate-device/SKILL.md +1 -1
  167. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(12)_acceptance-of-piv-credentials/SKILL.md +1 -1
  168. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(13)_out-of-band-authentication/SKILL.md +1 -1
  169. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(2)_multi-factor-authentication-to-non-privileged-accounts/SKILL.md +1 -1
  170. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(3)_local-access-to-privileged-accounts/SKILL.md +1 -1
  171. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(4)_local-access-to-non-privileged-accounts/SKILL.md +1 -1
  172. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(5)_individual-authentication-with-group-authentication/SKILL.md +1 -1
  173. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(6)_access-to-accounts-separate-device/SKILL.md +1 -1
  174. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(7)_network-access-to-non-privileged-accounts-separate-device/SKILL.md +1 -1
  175. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(8)_access-to-accounts-replay-resistant/SKILL.md +1 -1
  176. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2(9)_network-access-to-non-privileged-accounts-replay-resistant/SKILL.md +1 -1
  177. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-2_identification-and-authentication-organizational-users/SKILL.md +1 -1
  178. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-3(1)_cryptographic-bidirectional-authentication/SKILL.md +1 -1
  179. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-3(2)_cryptographic-bidirectional-network-authentication/SKILL.md +1 -1
  180. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-3(3)_dynamic-address-allocation/SKILL.md +1 -1
  181. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-3(4)_device-attestation/SKILL.md +1 -1
  182. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-3_device-identification-and-authentication/SKILL.md +1 -1
  183. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(1)_prohibit-account-identifiers-as-public-identifiers/SKILL.md +1 -1
  184. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(2)_supervisor-authorization/SKILL.md +1 -1
  185. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(3)_multiple-forms-of-certification/SKILL.md +1 -1
  186. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(4)_identify-user-status/SKILL.md +1 -1
  187. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(5)_dynamic-management/SKILL.md +1 -1
  188. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(6)_cross-organization-management/SKILL.md +1 -1
  189. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(7)_in-person-registration/SKILL.md +1 -1
  190. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(8)_pairwise-pseudonymous-identifiers/SKILL.md +1 -1
  191. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4(9)_attribute-maintenance-and-protection/SKILL.md +1 -1
  192. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-4_identifier-management/SKILL.md +1 -1
  193. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(1)_password-based-authentication/SKILL.md +1 -1
  194. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(10)_dynamic-credential-binding/SKILL.md +1 -1
  195. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(11)_hardware-token-based-authentication/SKILL.md +1 -1
  196. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(12)_biometric-authentication-performance/SKILL.md +1 -1
  197. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(13)_expiration-of-cached-authenticators/SKILL.md +1 -1
  198. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(14)_managing-content-of-pki-trust-stores/SKILL.md +1 -1
  199. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(15)_gsa-approved-products-and-services/SKILL.md +1 -1
  200. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(16)_in-person-or-trusted-external-party-authenticator-issuance/SKILL.md +1 -1
  201. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(17)_presentation-attack-detection-for-biometric-authenticators/SKILL.md +1 -1
  202. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(18)_password-managers/SKILL.md +1 -1
  203. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(2)_public-key-based-authentication/SKILL.md +1 -1
  204. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(3)_in-person-or-trusted-external-party-registration/SKILL.md +1 -1
  205. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(4)_automated-support-for-password-strength-determination/SKILL.md +1 -1
  206. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(5)_change-authenticators-prior-to-delivery/SKILL.md +1 -1
  207. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(6)_protection-of-authenticators/SKILL.md +1 -1
  208. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(7)_no-embedded-unencrypted-static-authenticators/SKILL.md +1 -1
  209. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(8)_multiple-system-accounts/SKILL.md +1 -1
  210. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5(9)_federated-credential-management/SKILL.md +1 -1
  211. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-5_authenticator-management/SKILL.md +1 -1
  212. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-6_authentication-feedback/SKILL.md +1 -1
  213. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-7_cryptographic-module-authentication/SKILL.md +1 -1
  214. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8(1)_acceptance-of-piv-credentials-from-other-agencies/SKILL.md +1 -1
  215. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8(2)_acceptance-of-external-authenticators/SKILL.md +1 -1
  216. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8(3)_use-of-ficam-approved-products/SKILL.md +1 -1
  217. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8(4)_use-of-defined-profiles/SKILL.md +1 -1
  218. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8(5)_acceptance-of-piv-i-credentials/SKILL.md +1 -1
  219. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8(6)_disassociability/SKILL.md +1 -1
  220. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-8_identification-and-authentication-non-organizational-users/SKILL.md +1 -1
  221. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-9(1)_information-exchange/SKILL.md +1 -1
  222. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-9(2)_transmission-of-decisions/SKILL.md +1 -1
  223. package/skill/NIST/SP800-53_rev5/IA_identification-and-authentication/IA-9_service-identification-and-authentication/SKILL.md +1 -1
  224. package/skill/NIST/generate_sp800_53.py +2 -2
  225. package/skill/SKILL_GUIDE.md +1 -1
@@ -106258,20 +106258,22 @@ import { chromium as chromium2 } from "playwright";
106258
106258
  // ../hackbrowser/src/stealth.ts
106259
106259
  import { existsSync as existsSync2 } from "fs";
106260
106260
  import { chromium } from "playwright";
106261
+ var PLATFORM_ARGS = process.platform === "linux" ? ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--no-zygote"] : [];
106261
106262
  var LAUNCH_ARGS = [
106262
106263
  "--disable-blink-features=AutomationControlled",
106263
106264
  "--disable-features=IsolateOrigins,site-per-process",
106264
- "--no-sandbox",
106265
- "--disable-setuid-sandbox",
106266
- "--disable-dev-shm-usage",
106267
106265
  "--no-first-run",
106268
- "--no-zygote"
106266
+ "--disable-backgrounding-occluded-windows",
106267
+ "--disable-renderer-backgrounding",
106268
+ "--disable-background-timer-throttling",
106269
+ "--disable-hang-monitor",
106270
+ "--disable-ipc-flooding-protection",
106271
+ "--disable-component-update",
106272
+ ...PLATFORM_ARGS
106269
106273
  ];
106270
106274
  function launchOptions(headless) {
106271
- return { headless, args: LAUNCH_ARGS };
106272
- }
106273
- function userAgent(version3) {
106274
- return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version3} Safari/537.36`;
106275
+ const args = headless ? LAUNCH_ARGS : [...LAUNCH_ARGS, "--start-maximized"];
106276
+ return { headless, args };
106275
106277
  }
106276
106278
  function findSystemChrome() {
106277
106279
  const candidates = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : process.platform === "win32" ? [
@@ -106289,10 +106291,9 @@ async function connect(opts) {
106289
106291
  return chromium.launch({ ...launchOptions(opts.headless), executablePath: chrome });
106290
106292
  return chromium.launch(launchOptions(opts.headless));
106291
106293
  }
106292
- function contextOptions(version3) {
106294
+ function contextOptions(headless = true) {
106293
106295
  return {
106294
- userAgent: userAgent(version3),
106295
- viewport: { width: 1920, height: 1080 },
106296
+ viewport: headless ? { width: 1920, height: 1080 } : null,
106296
106297
  screen: { width: 1920, height: 1080 },
106297
106298
  locale: "en-US",
106298
106299
  timezoneId: "America/New_York",
@@ -106341,34 +106342,24 @@ Object.defineProperty(screen, 'availWidth', { get: () => 1920 });
106341
106342
  Object.defineProperty(screen, 'availHeight', { get: () => 1040 });
106342
106343
  Object.defineProperty(screen, 'colorDepth', { get: () => 24 });
106343
106344
  Object.defineProperty(screen, 'pixelDepth', { get: () => 24 });
106344
- window.outerWidth = 1920;
106345
- window.outerHeight = 1080;
106346
- window.innerWidth = 1920;
106347
- window.innerHeight = 969;
106348
-
106349
- const origGetParameter = WebGLRenderingContext.prototype.getParameter;
106350
- WebGLRenderingContext.prototype.getParameter = function(param) {
106351
- if (param === 37445) return 'Intel Inc.';
106352
- if (param === 37446) return 'Intel Iris OpenGL Engine';
106353
- return origGetParameter.call(this, param);
106354
- };
106355
- const origGetParameter2 = WebGL2RenderingContext.prototype.getParameter;
106356
- WebGL2RenderingContext.prototype.getParameter = function(param) {
106357
- if (param === 37445) return 'Intel Inc.';
106358
- if (param === 37446) return 'Intel Iris OpenGL Engine';
106359
- return origGetParameter2.call(this, param);
106360
- };
106361
-
106362
- const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
106363
- HTMLCanvasElement.prototype.toDataURL = function(type, quality) {
106364
- const ctx = this.getContext('2d');
106365
- if (ctx) {
106366
- const pixel = ctx.getImageData(0, 0, 1, 1);
106367
- pixel.data[3] = pixel.data[3] ^ 1;
106368
- ctx.putImageData(pixel, 0, 0);
106369
- }
106370
- return origToDataURL.call(this, type, quality);
106371
- };
106345
+ // NOTE: window.inner/outerWidth/Height are intentionally NOT spoofed. They must reflect
106346
+ // the REAL window so (a) the crawler's own geometry stays correct — scanner occlusion /
106347
+ // offscreen / scroll and the injected panel/login-bar clamp all read innerWidth and
106348
+ // (b) they stay consistent with the actual rendered viewport (a faked 1920 over a smaller
106349
+ // real window is itself a detectable fingerprint mismatch). Monitor size is spoofed via
106350
+ // screen.* above; the window is launched at 1920x1080 (launchOptions/contextOptions) so on
106351
+ // a normal host innerWidth is already ~1920 with no spoof needed.
106352
+
106353
+ // NOTE: WebGL vendor/renderer are intentionally NOT spoofed. The real GPU string is a
106354
+ // genuine, self-consistent value; a faked 'Intel Iris OpenGL Engine' (an old macOS GPU
106355
+ // name, in the outdated non-ANGLE format) contradicts both the platform and modern Chrome's
106356
+ // 'ANGLE (...)' renderer format, which is more detectable than reporting the truth.
106357
+
106358
+ // NOTE: canvas toDataURL is intentionally NOT perturbed. The previous per-call XOR of one
106359
+ // pixel's alpha mutated the canvas on every read, so the same canvas hashed differently each
106360
+ // time — an unstable canvas is itself a bot signal (a real canvas is stable). Canvas-noise is
106361
+ // an anti-TRACKING measure, not anti-bot-detection (not what cleared #76); reporting the real,
106362
+ // stable canvas is the consistent choice, matching the UA/WebGL/window decisions above.
106372
106363
 
106373
106364
  const origQuery = window.Permissions?.prototype?.query;
106374
106365
  if (origQuery) {
@@ -116220,12 +116211,16 @@ function normalizeUrl(url3) {
116220
116211
  u.searchParams.sort();
116221
116212
  let path4 = u.pathname.replace(/\/+$/, "") || "/";
116222
116213
  let hash4 = u.hash;
116223
- if (hash4 && hash4 !== "#" && !hash4.startsWith("#/")) {
116224
- hash4 = "#/" + hash4.slice(1);
116225
- }
116226
- hash4 = hash4.replace(/\/+$/, "");
116227
- if (hash4 === "#")
116214
+ const routeLike = hash4.startsWith("#/") || hash4.startsWith("#!") || hash4.includes("=") || hash4.includes("&");
116215
+ if (routeLike) {
116216
+ if (hash4.startsWith("#/") || hash4.startsWith("#!/")) {
116217
+ hash4 = hash4.replace(/\/+$/, "");
116218
+ if (hash4 === "#")
116219
+ hash4 = "";
116220
+ }
116221
+ } else {
116228
116222
  hash4 = "";
116223
+ }
116229
116224
  return u.origin + path4 + u.search + hash4;
116230
116225
  } catch {
116231
116226
  return url3;
@@ -117202,9 +117197,10 @@ var PANEL_CSS = `
117202
117197
 
117203
117198
  /* =========================== Card base =========================== */
117204
117199
  .card {
117205
- position: absolute;
117200
+ position: fixed;
117206
117201
  bottom: 16px; right: 16px;
117207
117202
  width: 360px;
117203
+ max-width: calc(100vw - 32px);
117208
117204
  background: #0b0f14;
117209
117205
  border: 1px solid #1f2937;
117210
117206
  /* Non-blocking HUD: the agent's own observability overlay must NEVER intercept
@@ -118045,6 +118041,20 @@ function handle(ev) {
118045
118041
  }
118046
118042
  }
118047
118043
 
118044
+ // ---------- keep card inside viewport on resize ----------
118045
+ function clampCard() {
118046
+ var vv = window.visualViewport;
118047
+ var vw = vv ? vv.width : (document.documentElement.clientWidth || window.innerWidth);
118048
+ var vh = vv ? vv.height : (document.documentElement.clientHeight || window.innerHeight);
118049
+ var w = card.offsetWidth;
118050
+ var h = card.offsetHeight;
118051
+ card.style.right = Math.max(0, Math.min(16, vw - w - 16)) + 'px';
118052
+ card.style.bottom = Math.max(0, Math.min(16, vh - h - 16)) + 'px';
118053
+ }
118054
+ window.addEventListener('resize', clampCard);
118055
+ if (window.visualViewport) window.visualViewport.addEventListener('resize', clampCard);
118056
+ clampCard();
118057
+
118048
118058
  // ---------- render restored state ----------
118049
118059
  setHeader();
118050
118060
  setCounters();
@@ -128174,8 +128184,60 @@ var OVERLAY_ESCAPE_WAIT = 400;
128174
128184
  var OVERLAY_CLEAR_WAIT = 800;
128175
128185
  var SPA_RENDER_RETRY_WAIT = 600;
128176
128186
  var POST_GOTO_WAIT = 400;
128187
+ var NETWORK_IDLE_TIMEOUT = 1500;
128177
128188
  var MAX_INLINE_DEPTH = 2;
128178
128189
  var LOGIN_SUCCESS_PATTERN = /POST\s+.*\/(login|signin|authenticate)\S*\s+\[200\]/i;
128190
+ async function stabilizeAfterGoto(page) {
128191
+ await page.waitForTimeout(POST_GOTO_WAIT);
128192
+ await page.waitForLoadState("networkidle", { timeout: NETWORK_IDLE_TIMEOUT }).catch(() => {});
128193
+ }
128194
+ function createBrowserHealth() {
128195
+ return { dead: false, reason: "" };
128196
+ }
128197
+ function attachLifecycleHandlers(browser, page, health) {
128198
+ browser.on("disconnected", () => {
128199
+ health.dead = true;
128200
+ health.reason = "browser process disconnected";
128201
+ log7.error("browser disconnected — crawl will terminate");
128202
+ });
128203
+ page.on("close", () => {
128204
+ health.dead = true;
128205
+ health.reason = "page closed unexpectedly";
128206
+ log7.error("page closed — crawl will terminate");
128207
+ });
128208
+ page.on("crash", () => {
128209
+ health.dead = true;
128210
+ health.reason = "page renderer crashed";
128211
+ log7.error("page crashed — crawl will terminate");
128212
+ });
128213
+ }
128214
+ function isBrowserDead(health) {
128215
+ return health.dead;
128216
+ }
128217
+ var BROWSER_WAIT_TIMEOUT = 30 * 60 * 1000;
128218
+ function waitForBrowserClose(browser, signal) {
128219
+ return new Promise((resolve4) => {
128220
+ let resolved = false;
128221
+ const done = () => {
128222
+ if (resolved)
128223
+ return;
128224
+ resolved = true;
128225
+ clearTimeout(timer);
128226
+ resolve4();
128227
+ };
128228
+ const timer = setTimeout(done, BROWSER_WAIT_TIMEOUT);
128229
+ browser.on("disconnected", done);
128230
+ if (signal) {
128231
+ if (signal.aborted) {
128232
+ done();
128233
+ return;
128234
+ }
128235
+ signal.addEventListener("abort", done, { once: true });
128236
+ }
128237
+ if (!browser.isConnected())
128238
+ done();
128239
+ });
128240
+ }
128179
128241
  function triggerReDiscovery(globalState) {
128180
128242
  if (globalState.authPhase === "authenticated")
128181
128243
  return;
@@ -128537,6 +128599,7 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
128537
128599
  const taskQueue = [...plan.tasks];
128538
128600
  const seenKeys = new Set(elements.map((e2) => `${e2.role}::${e2.label}`));
128539
128601
  let steps = 0;
128602
+ let lastRevealingClick = null;
128540
128603
  while (taskQueue.length > 0 && steps < MAX_STEPS_PER_PAGE) {
128541
128604
  steps++;
128542
128605
  const task = taskQueue.shift();
@@ -128559,16 +128622,18 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
128559
128622
  if (task.type === "form") {
128560
128623
  await executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
128561
128624
  } else {
128562
- await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
128625
+ await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav, lastRevealingClick);
128563
128626
  }
128564
128627
  const postActionElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
128565
128628
  if (task.type === "click" && task.role === "combobox") {
128566
- const optionTasks = collectOptionTasks(postActionElements, seenKeys);
128629
+ const optionTasks = await collectComboboxOptions(page, postActionElements, seenKeys, pageUrl, globalState.visitedPages);
128567
128630
  if (optionTasks.length > 0) {
128568
128631
  taskQueue.unshift(...optionTasks);
128569
128632
  }
128570
128633
  }
128571
128634
  const hasNewElements = discoverNewElements(postActionElements, seenKeys);
128635
+ if (task.type === "click" && hasNewElements)
128636
+ lastRevealingClick = task;
128572
128637
  const hasStaleTargets = queueHasStaleTargets(taskQueue, postActionElements);
128573
128638
  if (hasNewElements || hasStaleTargets || occ.pending.length > 0) {
128574
128639
  const freshElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
@@ -128681,15 +128746,17 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
128681
128746
  if (task.type === "form") {
128682
128747
  await executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
128683
128748
  } else {
128684
- await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
128749
+ await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav, lastRevealingClick);
128685
128750
  }
128686
128751
  const postActionElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
128687
128752
  if (task.type === "click" && task.role === "combobox") {
128688
- const optionTasks = collectOptionTasks(postActionElements, seenKeys);
128753
+ const optionTasks = await collectComboboxOptions(page, postActionElements, seenKeys, pageUrl, globalState.visitedPages);
128689
128754
  if (optionTasks.length > 0)
128690
128755
  additionalQueue.unshift(...optionTasks);
128691
128756
  }
128692
128757
  const hasNewElements = discoverNewElements(postActionElements, seenKeys);
128758
+ if (task.type === "click" && hasNewElements)
128759
+ lastRevealingClick = task;
128693
128760
  const hasStaleTargets = queueHasStaleTargets(additionalQueue, postActionElements);
128694
128761
  if (hasNewElements || hasStaleTargets || occ.pending.length > 0) {
128695
128762
  const freshElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
@@ -128754,6 +128821,25 @@ async function recoverFromOcclusion(page, el, original, retry) {
128754
128821
  await page.waitForTimeout(OVERLAY_CLEAR_WAIT);
128755
128822
  return retry();
128756
128823
  }
128824
+ async function recoverFromClosedContainer(page, el, original, opener, interceptor, retry) {
128825
+ if (original.success || !opener)
128826
+ return original;
128827
+ if (opener.role === el.role && opener.label === el.label)
128828
+ return original;
128829
+ if ((await probeClickPoint(page, el.selector)).status !== "offscreen")
128830
+ return original;
128831
+ const fresh = await collectElements(page);
128832
+ const openerEl = resolveElement(fresh, opener.role, opener.label);
128833
+ if (!openerEl)
128834
+ return original;
128835
+ log7.debug("target offscreen — re-opening its container via opener, retrying once", {
128836
+ target: el.label,
128837
+ opener: opener.label
128838
+ });
128839
+ await execute(page, openerEl, "click", undefined, interceptor.setPendingUI, fresh.length);
128840
+ await page.waitForTimeout(POST_GOTO_WAIT);
128841
+ return retry();
128842
+ }
128757
128843
  async function diagnoseOcclusion(page, el, occ) {
128758
128844
  const probe = await probeClickPoint(page, el.selector);
128759
128845
  if (probe.status === "occluded" && !occ.signaled.has(probe.occluder.text)) {
@@ -128779,8 +128865,7 @@ async function handleNavigation(page, parentUrl, linksToEnqueue, inScope, global
128779
128865
  }
128780
128866
  }
128781
128867
  await page.goto(parentUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
128782
- await page.waitForTimeout(POST_GOTO_WAIT);
128783
- await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
128868
+ await stabilizeAfterGoto(page);
128784
128869
  }
128785
128870
  async function executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav) {
128786
128871
  for (const field of task.fields) {
@@ -128846,7 +128931,7 @@ async function executeFormTask(task, page, elements, interceptor, semanticAction
128846
128931
  if (result.navigated)
128847
128932
  await handleNavigation(page, pageUrl, linksToEnqueue, inScope, globalState, nav);
128848
128933
  }
128849
- async function executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav) {
128934
+ async function executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav, opener) {
128850
128935
  const el = resolveElement(elements, task.role, task.label);
128851
128936
  if (!el) {
128852
128937
  log7.debug("click target not found", { role: task.role, label: task.label });
@@ -128866,6 +128951,7 @@ async function executeClickTask(task, page, elements, interceptor, semanticActio
128866
128951
  });
128867
128952
  let result = await execute(page, el, "click", undefined, interceptor.setPendingUI, elements.length);
128868
128953
  result = await recoverFromOcclusion(page, el, result, () => execute(page, el, "click", undefined, interceptor.setPendingUI, elements.length));
128954
+ result = await recoverFromClosedContainer(page, el, result, opener, interceptor, () => execute(page, el, "click", undefined, interceptor.setPendingUI, elements.length));
128869
128955
  const occluded = !result.success && await diagnoseOcclusion(page, el, occ);
128870
128956
  interceptor.clearPendingTrigger();
128871
128957
  trackResult(result, interceptor, globalState, credentialId, task.triggersMutation, page);
@@ -128901,6 +128987,24 @@ function collectOptionTasks(elements, seenKeys) {
128901
128987
  }
128902
128988
  return tasks.slice(0, 1);
128903
128989
  }
128990
+ var COMBOBOX_PROBES = ["e", "a", "2", "0", " "];
128991
+ async function collectComboboxOptions(page, elements, seenKeys, pageUrl, visitedPages) {
128992
+ const inline = collectOptionTasks(elements, seenKeys);
128993
+ if (inline.length > 0)
128994
+ return inline;
128995
+ const search = page.locator(":focus");
128996
+ const typeable = await search.evaluate((el) => !!el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable)).catch(() => false);
128997
+ if (!typeable)
128998
+ return inline;
128999
+ for (const probe of COMBOBOX_PROBES) {
129000
+ await search.fill(probe).catch(() => {});
129001
+ await page.waitForTimeout(200);
129002
+ if (await page.locator("[role=option]:visible").count() > 0)
129003
+ break;
129004
+ }
129005
+ const revealed = filterVisitedLinks(await collectElements(page), pageUrl, visitedPages);
129006
+ return collectOptionTasks(revealed, seenKeys);
129007
+ }
128904
129008
  function discoverNewElements(elements, seenKeys) {
128905
129009
  let found = false;
128906
129010
  for (const el of elements) {
@@ -129190,6 +129294,12 @@ async function runMultiCredential(config3, credentials) {
129190
129294
  throw new Error(`AI model required: ${String(err)}`);
129191
129295
  }
129192
129296
  const browser = await connect({ cdp: config3.cdp, headless: config3.headless ?? false });
129297
+ const health = createBrowserHealth();
129298
+ browser.on("disconnected", () => {
129299
+ health.dead = true;
129300
+ health.reason = "browser process disconnected";
129301
+ log7.error("browser disconnected — multi-credential crawl will terminate");
129302
+ });
129193
129303
  let sessionId = config3.cyberstrike.sessionID ?? "";
129194
129304
  if (!dryRun && !sessionId) {
129195
129305
  const created = await initSession(serverUrl, targetUrl, undefined);
@@ -129203,14 +129313,46 @@ async function runMultiCredential(config3, credentials) {
129203
129313
  const captureQueues = new Map;
129204
129314
  const lastAuthHeaders = new Map;
129205
129315
  for (const [credIndex, cred] of credentials.entries()) {
129206
- const browserContext = await browser.newContext(contextOptions(browser.version()));
129316
+ const browserContext = await browser.newContext(contextOptions(config3.headless ?? false));
129207
129317
  await browserContext.addInitScript(INIT_SCRIPT);
129208
129318
  if (panelOn)
129209
129319
  await browserContext.addInitScript(PANEL_INIT_SCRIPT);
129210
129320
  const page = await browserContext.newPage();
129321
+ page.on("close", () => {
129322
+ health.dead = true;
129323
+ health.reason = `page closed (credential: ${cred.id})`;
129324
+ log7.error("page closed — multi-credential crawl will terminate", { credential: cred.id });
129325
+ });
129326
+ page.on("crash", () => {
129327
+ health.dead = true;
129328
+ health.reason = `page crashed (credential: ${cred.id})`;
129329
+ log7.error("page crashed — multi-credential crawl will terminate", { credential: cred.id });
129330
+ });
129211
129331
  attachDialogAutoAccept(page);
129212
129332
  attachFileChooserAutoFill(page);
129213
- await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
129333
+ const mcInitNavErr = await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 }).then(() => null).catch((e2) => e2);
129334
+ if (mcInitNavErr) {
129335
+ log7.warn("initial navigation failed", {
129336
+ credential: cred.id,
129337
+ url: targetUrl,
129338
+ err: mcInitNavErr.message.split(`
129339
+ `)[0]
129340
+ });
129341
+ if (!config3.headless && browser.isConnected()) {
129342
+ log7.info("browser stays open — navigate manually or close the window to finish");
129343
+ await waitForBrowserClose(browser, config3.signal);
129344
+ } else {
129345
+ await browser.close().catch(() => {});
129346
+ }
129347
+ return {
129348
+ sessionID: dryRun ? "" : sessionId,
129349
+ capturedEndpoints: 0,
129350
+ pagesExplored: 0,
129351
+ totalSteps: 0,
129352
+ errors: [`Initial navigation failed (credential: ${cred.id}): ${mcInitNavErr.message}`],
129353
+ usage: usageAcc
129354
+ };
129355
+ }
129214
129356
  csEmit(page, {
129215
129357
  type: "init",
129216
129358
  target: targetUrl,
@@ -129276,6 +129418,14 @@ async function runMultiCredential(config3, credentials) {
129276
129418
  globalState.authPhase = "authenticated";
129277
129419
  let pagesExplored = 0;
129278
129420
  while (pageQueue.length > 0 && pagesExplored < maxPages) {
129421
+ if (isBrowserDead(health)) {
129422
+ log7.error("browser died, terminating multi-credential crawl", {
129423
+ reason: health.reason,
129424
+ pagesExplored,
129425
+ captured: globalState.capturedEndpoints.size
129426
+ });
129427
+ break;
129428
+ }
129279
129429
  if (config3.signal?.aborted) {
129280
129430
  log7.info("multi-credential crawl cancelled by signal", { pagesExplored, queued: pageQueue.length });
129281
129431
  break;
@@ -129292,8 +129442,7 @@ async function runMultiCredential(config3, credentials) {
129292
129442
  const navigateResults = await Promise.all(activeContexts.map(async (ctx) => {
129293
129443
  try {
129294
129444
  await ctx.page.goto(entry.url, { waitUntil: "domcontentloaded", timeout: 15000 });
129295
- await ctx.page.waitForTimeout(POST_GOTO_WAIT);
129296
- await ctx.page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
129445
+ await stabilizeAfterGoto(ctx.page);
129297
129446
  return { ctx, success: true, redirected: normalizeUrl(ctx.page.url()) !== normalizeUrl(entry.url) };
129298
129447
  } catch (err) {
129299
129448
  log7.warn("navigation failed", { credential: ctx.id, url: entry.url, err: String(err) });
@@ -129364,15 +129513,31 @@ async function runMultiCredential(config3, credentials) {
129364
129513
  });
129365
129514
  }
129366
129515
  for (const ctx of visitableContexts) {
129516
+ if (isBrowserDead(health))
129517
+ break;
129367
129518
  log7.info("exploring", { credential: ctx.id, url: entry.url });
129368
129519
  const exploreInline = async (p, url3, depth) => {
129520
+ if (isBrowserDead(health))
129521
+ return;
129369
129522
  const found = await explorePageWithAI(p, url3, ctx.interceptor, model, globalState, inScope, ctx.id, maxPages, usageAcc, { explore: exploreInline, depth });
129370
129523
  for (const u of found)
129371
129524
  enqueueWithContext(u, ctx.id, pageQueue, visitedPages, inScope, pathPatternCounts);
129372
129525
  };
129373
- const discovered = await explorePageWithAI(ctx.page, entry.url, ctx.interceptor, model, globalState, inScope, ctx.id, maxPages, usageAcc, { explore: exploreInline, depth: 0 });
129374
- for (const url3 of discovered) {
129375
- enqueueWithContext(url3, ctx.id, pageQueue, visitedPages, inScope, pathPatternCounts);
129526
+ try {
129527
+ const discovered = await explorePageWithAI(ctx.page, entry.url, ctx.interceptor, model, globalState, inScope, ctx.id, maxPages, usageAcc, { explore: exploreInline, depth: 0 });
129528
+ for (const url3 of discovered) {
129529
+ enqueueWithContext(url3, ctx.id, pageQueue, visitedPages, inScope, pathPatternCounts);
129530
+ }
129531
+ } catch (err) {
129532
+ if (isBrowserDead(health)) {
129533
+ log7.error("exploration aborted — browser died mid-page", {
129534
+ credential: ctx.id,
129535
+ url: entry.url,
129536
+ reason: health.reason
129537
+ });
129538
+ break;
129539
+ }
129540
+ log7.warn("exploration error", { credential: ctx.id, url: entry.url, err: String(err) });
129376
129541
  }
129377
129542
  }
129378
129543
  for (const ctx of visitableContexts) {
@@ -129385,36 +129550,50 @@ async function runMultiCredential(config3, credentials) {
129385
129550
  await drainPageCaptures(captureQueues, captureHandlers, availability, entry.url, visitedBy);
129386
129551
  await visitableContexts[0]?.page.waitForTimeout(300);
129387
129552
  }
129388
- log7.info("multi-credential exploration complete, draining remaining requests");
129389
- await contexts[0]?.page.waitForTimeout(2000);
129553
+ const browserDied = isBrowserDead(health);
129554
+ log7.info(browserDied ? "browser died, draining captured requests" : "multi-credential exploration complete, draining remaining requests", {
129555
+ pagesExplored,
129556
+ captured: globalState.capturedEndpoints.size,
129557
+ ...browserDied ? { reason: health.reason } : {}
129558
+ });
129559
+ if (!browserDied)
129560
+ await contexts[0]?.page.waitForTimeout(2000).catch(() => {});
129390
129561
  await drainPageCaptures(captureQueues, captureHandlers, new Map, null, null);
129391
129562
  log7.info("multi-credential crawl done", {
129392
129563
  pagesExplored,
129393
129564
  credentials: contexts.map((c) => c.id),
129394
129565
  totalSteps: globalState.totalSteps,
129395
- capturedEndpoints: globalState.capturedEndpoints.size
129566
+ capturedEndpoints: globalState.capturedEndpoints.size,
129567
+ browserDied
129396
129568
  });
129397
- const mutationCount = [...globalState.capturedEndpoints].filter((e2) => /^(POST|PUT|PATCH|DELETE)\s/.test(e2)).length;
129398
- const credentialIds = contexts.map((c) => c.id);
129399
- for (const ctx of contexts) {
129400
- csEmit(ctx.page, {
129401
- type: "crawl-done",
129402
- summary: {
129403
- pagesExplored,
129404
- capturedEndpoints: globalState.capturedEndpoints.size,
129405
- mutations: mutationCount,
129406
- credentials: credentialIds
129407
- }
129408
- });
129569
+ if (!browserDied) {
129570
+ const mutationCount = [...globalState.capturedEndpoints].filter((e2) => /^(POST|PUT|PATCH|DELETE)\s/.test(e2)).length;
129571
+ const credentialIds = contexts.map((c) => c.id);
129572
+ for (const ctx of contexts) {
129573
+ csEmit(ctx.page, {
129574
+ type: "crawl-done",
129575
+ summary: {
129576
+ pagesExplored,
129577
+ capturedEndpoints: globalState.capturedEndpoints.size,
129578
+ mutations: mutationCount,
129579
+ credentials: credentialIds
129580
+ }
129581
+ });
129582
+ }
129583
+ await contexts[0]?.page.waitForTimeout(600).catch(() => {});
129584
+ }
129585
+ if (!config3.headless && browser.isConnected()) {
129586
+ log7.info("crawl complete — browser stays open, close the window to finish");
129587
+ await waitForBrowserClose(browser, config3.signal);
129588
+ } else if (!browserDied) {
129589
+ await browser.close().catch(() => {});
129409
129590
  }
129410
- await contexts[0]?.page.waitForTimeout(600).catch(() => {});
129411
- await browser.close();
129412
129591
  return {
129413
129592
  sessionID: dryRun ? "" : sessionId,
129414
129593
  capturedEndpoints: globalState.capturedEndpoints.size,
129415
129594
  pagesExplored,
129416
129595
  totalSteps: globalState.totalSteps,
129417
- errors: [],
129596
+ errors: browserDied ? [`Browser died: ${health.reason}. Captured ${globalState.capturedEndpoints.size} endpoints before failure.`] : [],
129418
129597
  usage: usageAcc
129419
129598
  };
129420
129599
  }
@@ -129456,11 +129635,13 @@ async function run(config3) {
129456
129635
  sessionID = created;
129457
129636
  }
129458
129637
  const browser = await connect({ cdp: config3.cdp, headless: config3.headless ?? false });
129459
- const context = await browser.newContext(contextOptions(browser.version()));
129638
+ const health = createBrowserHealth();
129639
+ const context = await browser.newContext(contextOptions(config3.headless ?? false));
129460
129640
  await context.addInitScript(INIT_SCRIPT);
129461
129641
  if (panelOn)
129462
129642
  await context.addInitScript(PANEL_INIT_SCRIPT);
129463
129643
  const page = await context.newPage();
129644
+ attachLifecycleHandlers(browser, page, health);
129464
129645
  attachDialogAutoAccept(page);
129465
129646
  attachFileChooserAutoFill(page);
129466
129647
  if (config3.auth.sessionFile) {
@@ -129485,191 +129666,258 @@ async function run(config3) {
129485
129666
  await handleCapture(captured);
129486
129667
  }
129487
129668
  }, 500);
129488
- await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
129489
- csEmit(page, {
129490
- type: "init",
129491
- target: targetUrl,
129492
- credentials: [],
129493
- maxPages,
129494
- startedAt: Date.now()
129495
- });
129496
- const isAuthenticated = config3.auth.authenticated || !!config3.auth.credentials;
129497
- if (config3.auth.authenticated) {
129498
- await waitForManualLogin(page);
129499
- if (!dryRun && credentialId) {
129500
- const registeredId = await registerCredential(serverUrl, sessionID, credentialId);
129501
- if (registeredId) {
129502
- credentialId = registeredId;
129503
- handleCapture = createIngestHandler(serverUrl, sessionID, credentialId);
129504
- }
129505
- }
129506
- } else if (config3.auth.credentials) {
129507
- await autoLogin(page, config3.auth.credentials);
129508
- } else {
129509
- await handle2FA(page);
129510
- }
129511
- const globalState = createGlobalState({ outOfScope: config3.outOfScope });
129512
- if (config3.outOfScope?.length) {
129513
- log7.info("out-of-scope labels", { count: config3.outOfScope.length, labels: config3.outOfScope });
129514
- }
129515
- if (isAuthenticated) {
129516
- globalState.authPhase = "authenticated";
129517
- }
129518
- globalState.visitedPages.add(normalizeUrl(page.url()));
129519
- globalState.pageQueue.push(page.url());
129520
- page.on("response", (response) => {
129521
- if (globalState.authPhase === "authenticated")
129522
- return;
129523
- const req = response.request();
129524
- const method = req.method();
129525
- const status = response.status();
129526
- try {
129527
- const pathname = new URL(req.url()).pathname;
129528
- if (LOGIN_SUCCESS_PATTERN.test(`${method} ${pathname} [${status}]`)) {
129529
- log7.info("login success detected via HTTP response");
129530
- triggerReDiscovery(globalState);
129669
+ try {
129670
+ const initNavErr = await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 }).then(() => null).catch((e2) => e2);
129671
+ if (initNavErr) {
129672
+ log7.warn("initial navigation failed", { url: targetUrl, err: initNavErr.message.split(`
129673
+ `)[0] });
129674
+ if (!config3.headless && browser.isConnected()) {
129675
+ log7.info("browser stays open — navigate manually or close the window to finish");
129676
+ await waitForBrowserClose(browser, config3.signal);
129677
+ } else {
129678
+ await browser.close().catch(() => {});
129531
129679
  }
129532
- } catch {}
129533
- });
129534
- let pagesExplored = 0;
129535
- while (globalState.pageQueue.length > 0 && pagesExplored < maxPages) {
129536
- if (config3.signal?.aborted) {
129537
- log7.info("crawl cancelled by signal", { pagesExplored, queued: globalState.pageQueue.length });
129538
- break;
129680
+ return {
129681
+ sessionID: dryRun ? "" : sessionID,
129682
+ capturedEndpoints: 0,
129683
+ pagesExplored: 0,
129684
+ totalSteps: 0,
129685
+ errors: [`Initial navigation failed: ${initNavErr.message}`],
129686
+ usage: usageAcc
129687
+ };
129539
129688
  }
129540
- const nextUrl = globalState.pageQueue.shift();
129541
- pagesExplored++;
129542
- if (page.url() !== nextUrl) {
129543
- const navErr = await page.goto(nextUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).then(() => null).catch((e2) => e2);
129544
- if (navErr) {
129545
- log7.warn("navigation failed", { url: nextUrl, err: navErr.message.split(`
129546
- `)[0] });
129547
- continue;
129689
+ csEmit(page, {
129690
+ type: "init",
129691
+ target: targetUrl,
129692
+ credentials: [],
129693
+ maxPages,
129694
+ startedAt: Date.now()
129695
+ });
129696
+ const isAuthenticated = config3.auth.authenticated || !!config3.auth.credentials;
129697
+ if (config3.auth.authenticated) {
129698
+ await waitForManualLogin(page);
129699
+ if (!dryRun && credentialId) {
129700
+ const registeredId = await registerCredential(serverUrl, sessionID, credentialId);
129701
+ if (registeredId) {
129702
+ credentialId = registeredId;
129703
+ handleCapture = createIngestHandler(serverUrl, sessionID, credentialId);
129704
+ }
129548
129705
  }
129549
- await page.waitForTimeout(POST_GOTO_WAIT);
129550
- await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
129706
+ } else if (config3.auth.credentials) {
129707
+ await autoLogin(page, config3.auth.credentials);
129708
+ } else {
129709
+ await handle2FA(page);
129551
129710
  }
129552
- if (!isInScope(page.url(), inScope)) {
129553
- log7.info("off-host redirect, skipping", { url: page.url() });
129554
- await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
129555
- continue;
129711
+ const globalState = createGlobalState({ outOfScope: config3.outOfScope });
129712
+ if (config3.outOfScope?.length) {
129713
+ log7.info("out-of-scope labels", { count: config3.outOfScope.length, labels: config3.outOfScope });
129556
129714
  }
129557
- const currentUrl = normalizeUrl(page.url());
129558
- if (currentUrl !== normalizeUrl(nextUrl) && globalState.visitedPages.has(currentUrl)) {
129559
- log7.info("redirect to already-explored page, skipping", { requested: normalizeUrl(nextUrl), landed: currentUrl });
129560
- continue;
129715
+ if (isAuthenticated) {
129716
+ globalState.authPhase = "authenticated";
129561
129717
  }
129562
- globalState.visitedPages.add(currentUrl);
129563
- log7.info("exploring page", { page: `${pagesExplored}/${maxPages}`, url: currentUrl });
129564
- log7.debug("state", {
129565
- phase: globalState.authPhase,
129566
- queueSize: globalState.pageQueue.length,
129567
- queue: globalState.pageQueue.slice(0, 5).map((u) => {
129568
- try {
129569
- return new URL(u).pathname + new URL(u).hash;
129570
- } catch {
129571
- return u;
129718
+ globalState.visitedPages.add(normalizeUrl(page.url()));
129719
+ globalState.pageQueue.push(page.url());
129720
+ page.on("response", (response) => {
129721
+ if (globalState.authPhase === "authenticated")
129722
+ return;
129723
+ const req = response.request();
129724
+ const method = req.method();
129725
+ const status = response.status();
129726
+ try {
129727
+ const pathname = new URL(req.url()).pathname;
129728
+ if (LOGIN_SUCCESS_PATTERN.test(`${method} ${pathname} [${status}]`)) {
129729
+ log7.info("login success detected via HTTP response");
129730
+ triggerReDiscovery(globalState);
129572
129731
  }
129573
- }),
129574
- visited: globalState.visitedPages.size,
129575
- deferred: globalState.deferredAuthPages.map((d) => `${d.type}:${d.url.split("/").pop()}`),
129576
- endpoints: globalState.capturedEndpoints.size,
129577
- steps: globalState.totalSteps
129578
- });
129579
- await page.keyboard.press("Escape").catch(() => {});
129580
- await page.waitForTimeout(OVERLAY_ESCAPE_WAIT);
129581
- if (await isViewportCenterBlocked(page)) {
129582
- const dims = await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }));
129583
- await page.mouse.click(dims.w / 2, dims.h / 2).catch(() => {});
129584
- await page.waitForTimeout(OVERLAY_ESCAPE_WAIT);
129585
- }
129586
- await dismissCookieBanner(page);
129587
- await revealLazyContent(page);
129588
- await expandDisclosures(page);
129589
- const intel = getIntelligence(globalState, SINGLE_CRED);
129590
- const oldFingerprint = intel.pageFingerprints.get(currentUrl);
129591
- log7.debug("fingerprint check", { url: currentUrl, hasOld: !!oldFingerprint });
129592
- if (oldFingerprint !== undefined) {
129593
- const currentElements = await collectElements(page);
129594
- const newFingerprint = generateFingerprint(currentElements);
129595
- log7.debug("fingerprint compare", {
129596
- url: currentUrl,
129597
- match: newFingerprint === oldFingerprint,
129598
- oldFp: oldFingerprint.slice(0, 80),
129599
- newFp: newFingerprint.slice(0, 80)
129600
- });
129601
- if (newFingerprint === oldFingerprint) {
129602
- log7.info("page unchanged after auth, skipping exploration", { url: currentUrl });
129603
- const domLinks = await collectDOMLinks(page, currentUrl, inScope);
129604
- for (const url3 of domLinks) {
129605
- enqueueUrl(url3, globalState, inScope);
129606
- }
129607
- intel.pageFingerprints.set(currentUrl, newFingerprint);
129608
- await page.waitForTimeout(300);
129732
+ } catch {}
129733
+ });
129734
+ let pagesExplored = 0;
129735
+ while (globalState.pageQueue.length > 0 && pagesExplored < maxPages) {
129736
+ if (isBrowserDead(health)) {
129737
+ log7.error("browser died, terminating crawl", {
129738
+ reason: health.reason,
129739
+ pagesExplored,
129740
+ captured: globalState.capturedEndpoints.size
129741
+ });
129742
+ break;
129743
+ }
129744
+ if (config3.signal?.aborted) {
129745
+ log7.info("crawl cancelled by signal", { pagesExplored, queued: globalState.pageQueue.length });
129746
+ break;
129747
+ }
129748
+ const nextUrl = globalState.pageQueue.shift();
129749
+ pagesExplored++;
129750
+ if (page.url() !== nextUrl) {
129751
+ const navErr = await page.goto(nextUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).then(() => null).catch((e2) => e2);
129752
+ if (navErr) {
129753
+ log7.warn("navigation failed", { url: nextUrl, err: navErr.message.split(`
129754
+ `)[0] });
129755
+ continue;
129756
+ }
129757
+ await stabilizeAfterGoto(page);
129758
+ }
129759
+ if (!isInScope(page.url(), inScope)) {
129760
+ log7.info("off-host redirect, skipping", { url: page.url() });
129761
+ await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
129609
129762
  continue;
129610
129763
  }
129611
- log7.info("page changed after auth, re-exploring", { url: currentUrl });
129612
- }
129613
- const exploreInline = async (p, url3, depth) => {
129614
- const found = await explorePageWithAI(p, url3, interceptor, model, globalState, inScope, SINGLE_CRED, maxPages, usageAcc, { explore: exploreInline, depth });
129615
- for (const u of found)
129616
- enqueueUrl(u, globalState, inScope);
129617
- };
129618
- const discovered = await explorePageWithAI(page, currentUrl, interceptor, model, globalState, inScope, SINGLE_CRED, maxPages, usageAcc, { explore: exploreInline, depth: 0 });
129619
- let newEnqueued = 0;
129620
- for (const url3 of discovered) {
129621
- if (enqueueUrl(url3, globalState, inScope))
129622
- newEnqueued++;
129623
- }
129624
- if (discovered.length > 0) {
129625
- log7.debug("discovered links", {
129626
- found: discovered.length,
129627
- enqueued: newEnqueued,
129628
- queueSize: globalState.pageQueue.length
129629
- });
129630
- }
129631
- flushReDiscovery(globalState);
129632
- await page.waitForTimeout(300);
129633
- if (globalState.pageQueue.length === 0 && globalState.deferredAuthPages.length > 0) {
129634
- log7.debug("queue empty, processing auth phase", {
129764
+ const currentUrl = normalizeUrl(page.url());
129765
+ if (currentUrl !== normalizeUrl(nextUrl) && globalState.visitedPages.has(currentUrl)) {
129766
+ log7.info("redirect to already-explored page, skipping", {
129767
+ requested: normalizeUrl(nextUrl),
129768
+ landed: currentUrl
129769
+ });
129770
+ continue;
129771
+ }
129772
+ globalState.visitedPages.add(currentUrl);
129773
+ log7.info("exploring page", { page: `${pagesExplored}/${maxPages}`, url: currentUrl });
129774
+ log7.debug("state", {
129635
129775
  phase: globalState.authPhase,
129636
- deferred: globalState.deferredAuthPages.map((d) => d.type)
129776
+ queueSize: globalState.pageQueue.length,
129777
+ queue: globalState.pageQueue.slice(0, 5).map((u) => {
129778
+ try {
129779
+ return new URL(u).pathname + new URL(u).hash;
129780
+ } catch {
129781
+ return u;
129782
+ }
129783
+ }),
129784
+ visited: globalState.visitedPages.size,
129785
+ deferred: globalState.deferredAuthPages.map((d) => `${d.type}:${d.url.split("/").pop()}`),
129786
+ endpoints: globalState.capturedEndpoints.size,
129787
+ steps: globalState.totalSteps
129637
129788
  });
129638
- processAuthPhase(globalState);
129789
+ await page.keyboard.press("Escape").catch(() => {});
129790
+ await page.waitForTimeout(OVERLAY_ESCAPE_WAIT);
129791
+ if (await isViewportCenterBlocked(page)) {
129792
+ const dims = await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }));
129793
+ await page.mouse.click(dims.w / 2, dims.h / 2).catch(() => {});
129794
+ await page.waitForTimeout(OVERLAY_ESCAPE_WAIT);
129795
+ }
129796
+ await dismissCookieBanner(page);
129797
+ await revealLazyContent(page);
129798
+ await expandDisclosures(page);
129799
+ const intel = getIntelligence(globalState, SINGLE_CRED);
129800
+ const oldFingerprint = intel.pageFingerprints.get(currentUrl);
129801
+ log7.debug("fingerprint check", { url: currentUrl, hasOld: !!oldFingerprint });
129802
+ if (oldFingerprint !== undefined) {
129803
+ const currentElements = await collectElements(page);
129804
+ const newFingerprint = generateFingerprint(currentElements);
129805
+ log7.debug("fingerprint compare", {
129806
+ url: currentUrl,
129807
+ match: newFingerprint === oldFingerprint,
129808
+ oldFp: oldFingerprint.slice(0, 80),
129809
+ newFp: newFingerprint.slice(0, 80)
129810
+ });
129811
+ if (newFingerprint === oldFingerprint) {
129812
+ log7.info("page unchanged after auth, skipping exploration", { url: currentUrl });
129813
+ const domLinks = await collectDOMLinks(page, currentUrl, inScope);
129814
+ for (const url3 of domLinks) {
129815
+ enqueueUrl(url3, globalState, inScope);
129816
+ }
129817
+ intel.pageFingerprints.set(currentUrl, newFingerprint);
129818
+ await page.waitForTimeout(300);
129819
+ continue;
129820
+ }
129821
+ log7.info("page changed after auth, re-exploring", { url: currentUrl });
129822
+ }
129823
+ const exploreInline = async (p, url3, depth) => {
129824
+ if (isBrowserDead(health))
129825
+ return;
129826
+ const found = await explorePageWithAI(p, url3, interceptor, model, globalState, inScope, SINGLE_CRED, maxPages, usageAcc, { explore: exploreInline, depth });
129827
+ for (const u of found)
129828
+ enqueueUrl(u, globalState, inScope);
129829
+ };
129830
+ let discovered = [];
129831
+ try {
129832
+ discovered = await explorePageWithAI(page, currentUrl, interceptor, model, globalState, inScope, SINGLE_CRED, maxPages, usageAcc, { explore: exploreInline, depth: 0 });
129833
+ } catch (err) {
129834
+ if (isBrowserDead(health)) {
129835
+ log7.error("exploration aborted — browser died mid-page", { url: currentUrl, reason: health.reason });
129836
+ break;
129837
+ }
129838
+ log7.warn("exploration error", { url: currentUrl, err: String(err) });
129839
+ }
129840
+ let newEnqueued = 0;
129841
+ for (const url3 of discovered) {
129842
+ if (enqueueUrl(url3, globalState, inScope))
129843
+ newEnqueued++;
129844
+ }
129845
+ if (discovered.length > 0) {
129846
+ log7.debug("discovered links", {
129847
+ found: discovered.length,
129848
+ enqueued: newEnqueued,
129849
+ queueSize: globalState.pageQueue.length
129850
+ });
129851
+ }
129852
+ flushReDiscovery(globalState);
129853
+ await page.waitForTimeout(300);
129854
+ if (globalState.pageQueue.length === 0 && globalState.deferredAuthPages.length > 0) {
129855
+ log7.debug("queue empty, processing auth phase", {
129856
+ phase: globalState.authPhase,
129857
+ deferred: globalState.deferredAuthPages.map((d) => d.type)
129858
+ });
129859
+ processAuthPhase(globalState);
129860
+ }
129639
129861
  }
129640
- }
129641
- log7.info("exploration complete, draining remaining requests");
129642
- await page.waitForTimeout(2000);
129643
- clearInterval(drainInterval);
129644
- while (captureQueue.length > 0) {
129645
- const captured = captureQueue.shift();
129646
- await handleCapture(captured);
129647
- }
129648
- log7.info("done", {
129649
- pagesExplored,
129650
- totalSteps: globalState.totalSteps,
129651
- capturedEndpoints: globalState.capturedEndpoints.size,
129652
- sessionID: dryRun ? undefined : sessionID
129653
- });
129654
- csEmit(page, {
129655
- type: "crawl-done",
129656
- summary: {
129862
+ const browserDied = isBrowserDead(health);
129863
+ log7.info(browserDied ? "browser died, draining captured requests" : "exploration complete, draining remaining requests", {
129864
+ pagesExplored,
129865
+ captured: globalState.capturedEndpoints.size,
129866
+ ...browserDied ? { reason: health.reason } : {}
129867
+ });
129868
+ if (!browserDied)
129869
+ await page.waitForTimeout(2000).catch(() => {});
129870
+ while (captureQueue.length > 0) {
129871
+ const captured = captureQueue.shift();
129872
+ await handleCapture(captured);
129873
+ }
129874
+ log7.info("done", {
129657
129875
  pagesExplored,
129876
+ totalSteps: globalState.totalSteps,
129658
129877
  capturedEndpoints: globalState.capturedEndpoints.size,
129659
- mutations: [...globalState.capturedEndpoints].filter((e2) => /^(POST|PUT|PATCH|DELETE)\s/.test(e2)).length,
129660
- credentials: [SINGLE_CRED]
129878
+ sessionID: dryRun ? undefined : sessionID,
129879
+ browserDied
129880
+ });
129881
+ if (!browserDied) {
129882
+ csEmit(page, {
129883
+ type: "crawl-done",
129884
+ summary: {
129885
+ pagesExplored,
129886
+ capturedEndpoints: globalState.capturedEndpoints.size,
129887
+ mutations: [...globalState.capturedEndpoints].filter((e2) => /^(POST|PUT|PATCH|DELETE)\s/.test(e2)).length,
129888
+ credentials: [SINGLE_CRED]
129889
+ }
129890
+ });
129891
+ await page.waitForTimeout(600).catch(() => {});
129892
+ while (captureQueue.length > 0) {
129893
+ const captured = captureQueue.shift();
129894
+ await handleCapture(captured);
129895
+ }
129896
+ }
129897
+ if (!config3.headless && browser.isConnected()) {
129898
+ log7.info("crawl complete — browser stays open, close the window to finish");
129899
+ const postCrawlDrain = setInterval(async () => {
129900
+ while (captureQueue.length > 0) {
129901
+ const captured = captureQueue.shift();
129902
+ await handleCapture(captured);
129903
+ }
129904
+ }, 500);
129905
+ await waitForBrowserClose(browser, config3.signal);
129906
+ clearInterval(postCrawlDrain);
129907
+ } else if (!browserDied) {
129908
+ await browser.close().catch(() => {});
129661
129909
  }
129662
- });
129663
- await page.waitForTimeout(600).catch(() => {});
129664
- await browser.close();
129665
- return {
129666
- sessionID: dryRun ? "" : sessionID,
129667
- capturedEndpoints: globalState.capturedEndpoints.size,
129668
- pagesExplored,
129669
- totalSteps: globalState.totalSteps,
129670
- errors: [],
129671
- usage: usageAcc
129672
- };
129910
+ return {
129911
+ sessionID: dryRun ? "" : sessionID,
129912
+ capturedEndpoints: globalState.capturedEndpoints.size,
129913
+ pagesExplored,
129914
+ totalSteps: globalState.totalSteps,
129915
+ errors: browserDied ? [`Browser died: ${health.reason}. Captured ${globalState.capturedEndpoints.size} endpoints before failure.`] : [],
129916
+ usage: usageAcc
129917
+ };
129918
+ } finally {
129919
+ clearInterval(drainInterval);
129920
+ }
129673
129921
  }
129674
129922
 
129675
129923
  // ../hackbrowser/src/api.ts
@@ -129962,7 +130210,11 @@ async function main() {
129962
130210
  });
129963
130211
  }
129964
130212
  });
129965
- await new Promise((resolve4) => rl.once("close", resolve4));
130213
+ await new Promise((resolve4) => rl.once("close", () => {
130214
+ controller.abort();
130215
+ resolve4();
130216
+ }));
130217
+ setTimeout(() => process.exit(0), 5000).unref();
129966
130218
  }
129967
130219
  async function runWorker(opts, signal) {
129968
130220
  const crawlOpts = buildCrawlOptions(opts, signal);