Last active
July 27, 2024 11:17
-
-
Save thiezn/eeb78dcdc3902cdb2f33f9050d6d429d to your computer and use it in GitHub Desktop.
Revisions
-
thiezn revised this gist
Nov 16, 2021 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -235,7 +235,7 @@ def _url(self, endpoint) -> str: def list_programs(self) -> Set[HackerOneProgram]: """Retrieve a list of programs.""" endpoint = "programs" programs = set() @@ -259,7 +259,7 @@ def list_programs(self) -> Set[HackerOneProgram]: def get_program(self, program_handle) -> HackerOneProgram: """Retrieve a program by handle.""" endpoint = f"programs/{program_handle}" response = self._get(endpoint) return HackerOneProgram.load_from_dict(response) @@ -270,7 +270,7 @@ def get_assets(self, program_handle) -> Set[HackerOneAsset]: This is a helper function to return only the assets on a program. Useful when you have retrieved a list of programs as this doesn't include assets. """ endpoint = f"programs/{program_handle}" response = self._get(endpoint) try: -
thiezn revised this gist
Oct 29, 2021 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -60,7 +60,7 @@ class HackerOneAssetType(Enum): HARDWARE = "HARDWARE" OTHER_APK = "OTHER_APK" OTHER_IPA = "OTHER_IPA" TESTFLIGHT = "TESTFLIGHT" @dataclass class HackerOneAsset: -
thiezn created this gist
Jul 18, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,294 @@ #!/usr/bin/env python3 """Interact with HackerOne Hacker API. First generate an API token through the Hackerone website and initialize the class: >>> username = "YOUR_USER_NAME" >>> token = "GENERATE_AN_API_TOKEN_THROUGH_HACKERONE_WEBSITE" >>> session = HackerOneSession(username, token) To retrieve a single program >>> hackerone_program = session.get_program("security") >>> print(hackerone_program) <HackerOneProgram HackerOne, 16 assets> >>> print(hackerone_program.program.offers_bounties) True To list all programs run the following. Note it will take some time to retrieve all programs. Please be concious of your fellow hackers and limit the amount of API calls you make. Note that when listing programs, the assets won't be returned. Use the get_program() or get_assets() API call for this. >>> all_programs = session.list_programs() >>> for program in all_programs: >>> print(program) <HackerOneProgram Node.js third-party modules, 0 assets> <HackerOneProgram Internet Freedom (IBB), 0 assets> ...truncated output... >>> for asset in session.list_assets(all_programs[0]): >>> print(asset) <HackerOneAsset URL api.example.com> """ import requests from dataclasses import dataclass from datetime import datetime from typing import Optional, Set from enum import Enum class HackerOneAssetType(Enum): """Class representing known types in HackerOne assets.""" URL = "URL" OTHER = "OTHER" GOOGLE_PLAY_APP_ID = "GOOGLE_PLAY_APP_ID" APPLE_STORE_APP_ID = "APPLE_STORE_APP_ID" WINDOWS_APP_STORE_APP_ID = "WINDOWS_APP_STORE_APP_ID" CIDR = "CIDR" SOURCE_CODE = "SOURCE_CODE" DOWNLOADABLE_EXECUTABLES = "DOWNLOADABLE_EXECUTABLES" HARDWARE = "HARDWARE" OTHER_APK = "OTHER_APK" OTHER_IPA = "OTHER_IPA" @dataclass class HackerOneAsset: """Class representing an asset of a HackerOne Program.""" id: str type: HackerOneAssetType identifier: str eligible_for_bounty: bool eligible_for_submission: bool max_severity: str created_at: datetime updated_at: datetime instuction: Optional[str] reference: Optional[str] confidentiality_requirement: Optional[str] integrity_requirement: Optional[str] availability_requirement: Optional[str] def __repr__(self) -> str: """Pretty representation of class instance.""" return f"<HackerOneAsset {self.type} {len(self.identifier)}>" @classmethod def load_from_dict(cls, asset_dict: dict): """Initialize class instance from Dictionary object.""" return cls( asset_dict["id"], HackerOneAssetType(asset_dict["attributes"]["asset_type"]), asset_dict["attributes"]["asset_identifier"], asset_dict["attributes"]["eligible_for_bounty"], asset_dict["attributes"]["eligible_for_submission"], asset_dict["attributes"]["max_severity"], datetime.fromisoformat(asset_dict["attributes"]["created_at"].rstrip("Z")), datetime.fromisoformat(asset_dict["attributes"]["updated_at"].rstrip("Z")), asset_dict["attributes"].get("instruction"), asset_dict["attributes"].get("reference"), asset_dict["attributes"].get("confidentiality_requirement"), asset_dict["attributes"].get("integrity_requirement"), asset_dict["attributes"].get("availability_requirement"), ) def __hash__(self): """Allow for use in Python Sets.""" return hash(self.id) def __eq__(self, other): """Compare two class instances.""" if other.id == self.id: return True return False @dataclass class HackerOneProgram: """Class representing a single HackerOne Program.""" id: str # Program attributes handle: str name: str currency: str profile_picture: str submission_state: str triage_active: str state: str started_accepting_at: datetime number_of_reports_for_user: int number_of_valid_reports_for_user: int bounty_earned_for_user: float last_invitation_accepted_at_for_user: Optional[str] bookmarked: bool allows_bounty_splitting: bool offers_bounties: bool # Assets assets: Set[HackerOneAsset] def __repr__(self) -> str: """Pretty representation of class instance.""" return f"<HackerOneProgram {self.name}, {len(self.assets)} assets>" @property def program_url(self) -> str: """The URL to the program on HackerOne.""" return f"https://hackerone.com/{self.handle}?type=team" @classmethod def load_from_dict(cls, program_dict: dict): """Initialize class instance from Dictionary object.""" try: assets = { HackerOneAsset.load_from_dict(asset) for asset in program_dict["relationships"]["structured_scopes"]["data"] } except KeyError: # When listing programs the assets are not returned. assets = set() return cls( program_dict["id"], program_dict["attributes"]["handle"], program_dict["attributes"]["name"], program_dict["attributes"]["currency"], program_dict["attributes"]["profile_picture"], program_dict["attributes"]["submission_state"], program_dict["attributes"]["triage_active"], program_dict["attributes"]["state"], datetime.fromisoformat( program_dict["attributes"]["started_accepting_at"].rstrip("Z") ), program_dict["attributes"]["number_of_reports_for_user"], program_dict["attributes"]["number_of_valid_reports_for_user"], program_dict["attributes"]["bounty_earned_for_user"], program_dict["attributes"]["last_invitation_accepted_at_for_user"], program_dict["attributes"]["bookmarked"], program_dict["attributes"]["allows_bounty_splitting"], program_dict["attributes"]["offers_bounties"], assets, ) def __hash__(self): """Allow for use in Python Sets.""" return hash(self.id) def __eq__(self, other): """Compare two class instances.""" if other.id == self.id: return True return False class HackerOneSession: """Class to interact with the Hacker API of HackerOne.""" def __init__(self, username, token, version="v1"): self._session = requests.session() self.version = version headers = { "Content-Type": "application/json", "Accept": "application/json", "Hello": "HackerOne!", } self._session.auth = (username, token) self._session.headers.update(headers) def _process_response(self, response): """Process HTTP response returned from API.""" if not response.ok: # TODO: Shall we sleep and retry on 'response.status_code == 429'? raise IOError(f"HTTP {response.status_code} {response.request.url}") return response.json() def _get(self, endpoint, params: dict = None): """Retrieve a HTTP GET endpoint.""" url = self._url(endpoint) response = self._session.get(url, params=params) return self._process_response(response) def _url(self, endpoint) -> str: """Generate full API url.""" url = f"https://api.hackerone.com/{self.version}/hackers/{endpoint}" return url def list_programs(self) -> Set[HackerOneProgram]: """Retrieve a list of programs.""" endpoint = "/programs" programs = set() page_number = 1 while True: response = self._get(endpoint, params={"page[number]": page_number}) if not response["links"].get("next") or not response.get("data"): break else: page_number += 1 programs.update( [ HackerOneProgram.load_from_dict(program) for program in response["data"] ] ) return programs def get_program(self, program_handle) -> HackerOneProgram: """Retrieve a program by handle.""" endpoint = f"/programs/{program_handle}" response = self._get(endpoint) return HackerOneProgram.load_from_dict(response) def get_assets(self, program_handle) -> Set[HackerOneAsset]: """Get the assets of given program. This is a helper function to return only the assets on a program. Useful when you have retrieved a list of programs as this doesn't include assets. """ endpoint = f"/programs/{program_handle}" response = self._get(endpoint) try: assets = { HackerOneAsset.load_from_dict(asset) for asset in response["relationships"]["structured_scopes"]["data"] } except KeyError: # When listing programs the assets are not returned. assets = set() return assets if __name__ == "__main__": from getpass import getpass username = input("Hackerone username: ").strip() token = getpass(f"{username} token: ").strip() session = HackerOneSession(username, token) print(session.get_program("security"))