Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
how a consumer would use the library or CLI tool (e.g. adding unit tests, updating documentation, etc) are not captured
here.

## Unreleased

### Updated
- The Incydr SDK and CLI now rely on Pydantic v2, instead of previously when they used v1. This means that the methods available on the models accepted and returned by many SDK methods have changed in some small ways. For most SDK and CLI workflows, no changes will need to be made to accommodate this upgrade. Details of the transition may be found [in Pydantic's documentation](https://docs.pydantic.dev/dev/migration/).

## 2.6.0 - 2025-07-23

### Added
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ dependencies = [
"requests",
"requests-toolbelt",
"rich",
"pydantic[dotenv]==1.*",
"pydantic>=2.11",
"pydantic-settings",
"isodate",
"python-dateutil",
]
Expand Down
8 changes: 4 additions & 4 deletions src/_incydr_cli/cmds/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,13 @@ def bulk_update_state(

class AlertBulkCSV(CSVModel):
alert_id: str = Field(csv_aliases=["id", "alert_id"])
state: state_type = Field(csv_aliases=["state"])
note: Optional[str]
state: state_type = Field(None, csv_aliases=["state"]) # type: ignore
note: Optional[str] = None

class AlertBulkJSON(Model):
alert_id: str = Field(alias="id")
state: state_type
note: Optional[str]
state: state_type = None # type: ignore
note: Optional[str] = None

client = Client()
if format_ == "csv":
Expand Down
12 changes: 6 additions & 6 deletions src/_incydr_cli/cmds/audit_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,9 @@ def _get_cursor_store(api_key):


class DefaultAuditEvent(Model):
type: str = Field(None, alias="type$")
actor_id: str = Field(None, alias="actorId")
actor_name: str = Field(None, alias="actorName")
actor_agent: str = Field(None, alias="actorAgent")
actor_ip_addresses: str = Field(None, alias="actorIpAddress")
timestamp: str
type: Optional[str] = Field(None, alias="type$")
actor_id: Optional[str] = Field(None, alias="actorId")
actor_name: Optional[str] = Field(None, alias="actorName")
actor_agent: Optional[str] = Field(None, alias="actorAgent")
actor_ip_addresses: Optional[str] = Field(None, alias="actorIpAddress")
timestamp: str = None
5 changes: 3 additions & 2 deletions src/_incydr_cli/cmds/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import click
from pydantic import Field
from pydantic import root_validator
from pydantic import model_validator
from requests.exceptions import HTTPError
from rich.panel import Panel
from rich.progress import track
Expand Down Expand Up @@ -51,7 +51,8 @@ class FileEventCSV(CSVModel):
class FileEventJSON(FileEventV2):
event_id: str = Field(alias="eventId")

@root_validator(pre=True)
@model_validator(mode="before")
@classmethod
def _event_id_required(cls, values): # noqa
# check if input is V2 file event
event = values.get("event")
Expand Down
14 changes: 8 additions & 6 deletions src/_incydr_cli/cmds/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional

from pydantic import Field
from pydantic import root_validator
from pydantic import model_validator

from _incydr_sdk.core.models import CSVModel
from _incydr_sdk.core.models import Model
Expand All @@ -12,10 +12,11 @@ class UserCSV(CSVModel):


class UserJSON(Model):
username: Optional[str]
userId: Optional[str]
username: Optional[str] = None
userId: Optional[str] = None

@root_validator(pre=True)
@model_validator(mode="before")
@classmethod
def _validate(cls, values): # noqa
if "username" not in values and "userId" not in values:
raise ValueError("A json key of 'username' or 'userId' is required")
Expand All @@ -31,9 +32,10 @@ class AgentCSV(CSVModel):


class AgentJSON(Model):
agent_id: Optional[str]
agent_id: Optional[str] = None

@root_validator(pre=True)
@model_validator(mode="before")
@classmethod
def _validate(cls, values): # noqa
if "agentGuid" in values:
values["agent_id"] = values["agentGuid"]
Expand Down
10 changes: 5 additions & 5 deletions src/_incydr_cli/cmds/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,14 @@ def bulk_update_state(
# Validate input models

class SessionCSV(CSVModel):
session_id: str = Field(csv_aliases=["session_id", "sessionId"])
state: state_type = Field(csv_aliases=["state"])
note: Optional[str]
session_id: str = Field(None, csv_aliases=["session_id", "sessionId"])
state: state_type = Field(None, csv_aliases=["state"]) # type: ignore
note: Optional[str] = None

class SessionJSON(Model):
session_id: str = Field(alias="sessionId")
state: state_type
note: Optional[str]
state: state_type = None # type: ignore
note: Optional[str] = None

if format_ == "csv":
models = SessionCSV.parse_csv(file)
Expand Down
51 changes: 27 additions & 24 deletions src/_incydr_sdk/actors/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@


class QueryActorsRequest(BaseModel):
nameStartsWith: Optional[str]
nameEndsWith: Optional[str]
active: Optional[bool]
pageSize: Optional[int]
page: Optional[int]
nameStartsWith: Optional[str] = None
nameEndsWith: Optional[str] = None
active: Optional[bool] = None
pageSize: Optional[int] = None
page: Optional[int] = None


class Actor(ResponseModel):
Expand Down Expand Up @@ -43,90 +43,93 @@ class Actor(ResponseModel):
"""

active: Optional[bool] = Field(
None, description="Whether or not the actor is active.", example=True
None, description="Whether or not the actor is active.", examples=[True]
)
actor_id: Optional[str] = Field(
None, alias="actorId", description="The actor ID.", example="112233445566"
None, alias="actorId", description="The actor ID.", examples=["112233445566"]
)
alternate_names: Optional[List[str]] = Field(
None,
alias="alternateNames",
description="A list of other names the actor may have.",
example=["johnsmith@gmail.com", "john-smith@company.com"],
examples=[["johnsmith@gmail.com", "john-smith@company.com"]],
)
country: Optional[str] = Field(
None, description="The actor's country", example="United States"
None, description="The actor's country", examples=["United States"]
)
department: Optional[str] = Field(
None, description="The actor's department", example="Product"
None, description="The actor's department", examples=["Product"]
)
division: Optional[str] = Field(
None, description="The actor's division", example="Engineering"
None, description="The actor's division", examples=["Engineering"]
)
employee_type: Optional[str] = Field(
None,
alias="employeeType",
description="The actor's employment, such as if they're a contractor.",
example="full-time",
examples=["full-time"],
)
end_date: Optional[str] = Field(
None, alias="endDate", description="The actor's end date.", example="2024-09-18"
None,
alias="endDate",
description="The actor's end date.",
examples=["2024-09-18"],
)
first_name: Optional[str] = Field(
None,
alias="firstName",
description="The first name of the actor.",
example="Smith",
examples=["Smith"],
)
in_scope: Optional[bool] = Field(
None,
alias="inScope",
description="The actor's scope state. An actor is considered 'in scope' if their activity is monitored in at least one data source.",
example=True,
examples=[True],
)
last_name: Optional[str] = Field(
None,
alias="lastName",
description="The last name of the actor.",
example="John",
examples=["John"],
)
locality: Optional[str] = Field(
None, description="The actor's locality (city).", example="Minneapolis"
None, description="The actor's locality (city).", examples=["Minneapolis"]
)
manager_actor_id: Optional[str] = Field(
None,
alias="managerActorId",
description="The actor ID of the actor's manager",
example="9988776655",
examples=["9988776655"],
)
name: Optional[str] = Field(
None,
description="The actor's (eg. full username/email) name.",
example="john.smith@gmail.com",
examples=["john.smith@gmail.com"],
)
notes: Optional[str] = Field(
None,
alias="notes",
description="Notes about the actor.",
example="A super cool person.",
examples=["A super cool person."],
)
parent_actor_id: Optional[str] = Field(
None,
alias="parentActorId",
description="The actor ID of this actor's parent actor. (if the actor has a parent).",
example="442244224422",
examples=["442244224422"],
)
region: Optional[str] = Field(
None, description="The actor's region.", example="Minnesota"
None, description="The actor's region.", examples=["Minnesota"]
)
start_date: Optional[str] = Field(
None,
alias="startDate",
description="The actor's start date.",
example="2024-09-18",
examples=["2024-09-18"],
)
title: Optional[str] = Field(
None, description="The actor's job title", example="Software Engineer"
None, description="The actor's job title", examples=["Software Engineer"]
)


Expand Down
23 changes: 12 additions & 11 deletions src/_incydr_sdk/agents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from pydantic import BaseModel
from pydantic import Field
from pydantic import validator
from pydantic import field_validator

from _incydr_sdk.core.models import ResponseModel
from _incydr_sdk.enums import _Enum
Expand Down Expand Up @@ -98,16 +98,17 @@ class AgentUpdateRequest(BaseModel):


class QueryAgentsRequest(BaseModel):
active: Optional[bool]
agentType: Optional[Union[AgentType, str]]
agentHealthy: Optional[bool]
anyOfAgentHealthIssueTypes: Optional[List[str]]
srtKey: Optional[Union[SortKeys, str]]
srtDir: Optional[str]
pageSize: Optional[int]
page: Optional[int]

@validator("srtDir")
active: Optional[bool] = None
agentType: Optional[Union[AgentType, str]] = None
agentHealthy: Optional[bool] = None
anyOfAgentHealthIssueTypes: Optional[List[str]] = None
srtKey: Optional[Union[SortKeys, str]] = None
srtDir: Optional[str] = None
pageSize: Optional[int] = None
page: Optional[int] = None

@field_validator("srtDir")
@classmethod
def _validate(cls, value): # noqa
value = str(value).upper()
assert value in ("ASC", "DESC")
Expand Down
Loading