diff --git a/.gitignore b/.gitignore index ced4d79..d835809 100644 --- a/.gitignore +++ b/.gitignore @@ -207,4 +207,5 @@ marimo/_lsp/ __marimo__/ # Development files -src/scratchpad.py +managedsat_scratchpad.py +siem_scratchpad.py diff --git a/README.md b/README.md index 3612f5a..3a12ce6 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ siem_api_client = HuntressSIEMAPIClient( Endpoints are 1:1 to what's available for both the Huntress Managed SAT and Huntress SIEM. For more information, check out the following resources: -- [Huntress Managed SAT REST API Docs](https://support.meetgradient.com/huntress-managed-sat) +- [Huntress Managed SAT REST API Docs](https://curricula.stoplight.io/docs/curricula-api/00fkcnpgk5vnn-getting-started) - [Huntress SIEM REST API Docs](https://api.huntress.io/docs) ### Get many diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8f216fd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,37 @@ +[project] +name = "pyhuntress" +version = "0.1.1" +authors = [ + { name="Peter Annabel", email="peter.annabel@gmail.com" }, +] +description = "A full-featured Python client for the Huntress APIs" +readme = "README.md" +requires-python = ">=3.9" +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.10", +] +keywords = [ + "Huntress", + "Manages SAT", + "SIEM", + "API", + "Python", + "Client", + "Annotated", + "Typed", + "MSP", +] +license = "GPL-3.0-only" +license-files = ["LICEN[CS]E*"] + +[project.urls] +Homepage = "https://github.com/brygphilomena/pyhuntress" +Issues = "https://github.com/brygphilomena/pyhuntress/issues" + +[build-system] +requires = ["hatchling >= 1.26"] +build-backend = "hatchling.build" \ No newline at end of file diff --git a/src/pyhuntress/models/siem/__init__.py b/src/pyhuntress/models/siem/__init__.py index 1e0e775..656a291 100644 --- a/src/pyhuntress/models/siem/__init__.py +++ b/src/pyhuntress/models/siem/__init__.py @@ -21,7 +21,7 @@ class SIEMAgents(HuntressModel): id: int | None = Field(default=None, alias="Id") version: str | None = Field(default=None, alias="Version") arch: str | None = Field(default=None, alias="Arch") - win_build_number: str | None = Field(default=None, alias="WinBuildNumber") + win_build_number: int | None = Field(default=None, alias="WinBuildNumber") domain_name: str | None = Field(default=None, alias="DomainName") created_at: datetime | None = Field(default=None, alias="CreateAt") hostname: str | None = Field(default=None, alias="Hostname") @@ -62,7 +62,10 @@ class SIEMAccount(HuntressModel): id: int | None = Field(default=None, alias="Id") name: str | None = Field(default=None, alias="Name") subdomain: str | None = Field(default=None, alias="Subdomain") - status: str | None = Field(default=None, alias="Status") + status: Literal[ + "enabled", + "disabled", + ] | None = Field(default=None, alias="Status") class SIEMActorResponse(HuntressModel): account: dict[str, Any] | None = Field(default=None, alias="Account") @@ -217,12 +220,14 @@ class SIEMReports(HuntressModel): itdr_incidents_reported: int | None = Field(default=None, alias="ITDRIncidentsReported") siem_incidents_reported: int | None = Field(default=None, alias="SIEMIncidentsReported") incidents_resolved: int | None = Field(default=None, alias="IncidentsResolved") + # The following 3 counts are listed as "map" in Huntress' docs, I'm not sure what data type to use here incident_severity_counts: int | None = Field(default=None, alias="IncidentSeverityCounts") incident_product_counts: int | None = Field(default=None, alias="IncidentProductCounts") incident_indicator_counts: int | None = Field(default=None, alias="IncidentIndicatorCounts") top_incident_av_threats: list | None = Field(default=None, alias="TopIncidentAVThreats") + # top_incident_hosts is also listed as "map" in their docs top_incident_hosts: list | None = Field(default=None, alias="TopIncidentHosts") - potential_threat_indicators: list | None = Field(default=None, alias="PotentialThreatIndicators") + potential_threat_indicators: int | None = Field(default=None, alias="PotentialThreatIndicators") agents_count: int | None = Field(default=None, alias="AgentsCount") deployed_canaries_count: int | None = Field(default=None, alias="DeployedCanariesCount") protected_profiles_count: int | None = Field(default=None, alias="ProtectedProfilesCount") @@ -232,6 +237,8 @@ class SIEMReports(HuntressModel): analyst_note: str | None = Field(default=None, alias="AnalystNote") global_threats_note: str | None = Field(default=None, alias="GlobalThreatsNote") ransomware_note: str | None = Field(default=None, alias="RansomwareNote") + # Huntress has incident_log listed as "complex" with the note "A JSON representation of any critical + # or high severity incidents from this report" incident_log: str | None = Field(default=None, alias="IncidentLog") total_mav_detection_count: int | None = Field(default=None, alias="TotalMAVDetectionCount") blocked_malware_count: int | None = Field(default=None, alias="BlockedMalwareCount") @@ -245,9 +252,9 @@ class SIEMReports(HuntressModel): itdr_signals: int | None = Field(default=None, alias="ITDRSignals") siem_signals: int | None = Field(default=None, alias="SIEMSignals") itdr_investigations_completed: int | None = Field(default=None, alias="ITDRInvestigationsCompleted") - macos_agents: str | None = Field(default=None, alias="MacOSAgents") - windows_agents: str | None = Field(default=None, alias="WindowsAgents") - only_macos_agents: str | None = Field(default=None, alias="OnlyMacOSAgents") + macos_agents: bool | None = Field(default=None, alias="MacOSAgents") + windows_agents: bool | None = Field(default=None, alias="WindowsAgents") + only_macos_agents: bool | None = Field(default=None, alias="OnlyMacOSAgents") antivirus_exclusions_count: int | None = Field(default=None, alias="AntivirusExclusionsCount") new_exclusions_count: int | None = Field(default=None, alias="NewExclusionsCount") allowed_exclusions_count: int | None = Field(default=None, alias="AllowedExclusionsCount") diff --git a/src/scratchpad.py b/src/scratchpad.py deleted file mode 100644 index f3cc957..0000000 --- a/src/scratchpad.py +++ /dev/null @@ -1,75 +0,0 @@ -import os -from pyhuntress import HuntressSIEMAPIClient -from dotenv import load_dotenv - -load_dotenv() - -siem_url = os.getenv('siem_url') -publickey = os.getenv('publickey') -privatekey = os.getenv('privatekey') - -# init client -siem_api_client = HuntressSIEMAPIClient( - siem_url, - publickey, - privatekey, -) - -#account = siem_api_client.account.get() -#print(account) - -#actor = siem_api_client.actor.get() -#print(actor) - -#agents = siem_api_client.agents.get() -#print(agents) - -#agents = siem_api_client.agents.id(3989773).get() -#print(agents) - -#billingreports = siem_api_client.billing_reports.get() -#print(billingreports) - -#billingreports = siem_api_client.billing_reports.id(90173).get() -#print(billingreports) - -#incidentreports = siem_api_client.incident_reports.get() -#print(incidentreports) - -incidentreports = siem_api_client.incident_reports.id(1448648).get() -print(incidentreports) - -#organizations = siem_api_client.organizations.id(174759).get() -#print(organizations) - -#organizations = siem_api_client.organizations.get() -#print(organizations) - -#reports = siem_api_client.reports.get() -#print(reports) - -#signals = siem_api_client.signals.get() -#print(signals) - -reports = siem_api_client.reports.id(2175766).get() -print(reports) - -signals = siem_api_client.signals.id(36581548).get() -print(signals) - -#paginated_billingreports = siem_api_client.billing_reports.paginated(1, 10) -#print(paginated_billingreports.data) - -#paginated_incidentreports = siem_api_client.incident_reports.paginated(1, 10) -#print(paginated_incidentreports.data) - -#paginated_organizations = siem_api_client.organizations.paginated(2, 100) -#print(paginated_organizations.data) -#paginated_organizations.get_next_page() -#print(paginated_organizations.data) - -#paginated_reports = siem_api_client.reports.paginated(1, 10) -#print(paginated_reports.data) - -#paginated_signals = siem_api_client.signals.paginated(1, 10) -#print(paginated_signals.data)