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
2 changes: 1 addition & 1 deletion services/api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ RUN set -eux; \
pip install --no-cache-dir \
"fastapi>=0.111,<0.112" \
"uvicorn[standard]>=0.29,<0.30" \
"pydantic>=2.7,<2.11" \
"pydantic[email]>=2.7,<2.14" \
"pydantic-settings>=2.2,<3" \
"sqlalchemy[asyncio]>=2.0.30,<3" \
"asyncpg>=0.29,<0.32" \
Expand Down
4 changes: 2 additions & 2 deletions services/api/app/api/v1/endpoints/attack_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ async def get_attack_chain(
text(
"""
INSERT INTO aisoc_attack_chains (
tenant_id, seed_alert_id, case_id, window,
tenant_id, seed_alert_id, case_id, "window",
chain, entity_graph, chain_signature, confidence,
updated_at
) VALUES (
:tenant_id, :seed_alert_id, :case_id, :window,
CAST(:chain AS JSONB), CAST(:entity_graph AS JSONB),
:signature, :confidence, NOW()
)
ON CONFLICT (tenant_id, seed_alert_id, window, chain_signature)
ON CONFLICT (tenant_id, seed_alert_id, "window", chain_signature)
DO UPDATE SET
case_id = EXCLUDED.case_id,
chain = EXCLUDED.chain,
Expand Down
9 changes: 6 additions & 3 deletions services/api/migrations/041_attack_chains.sql
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,20 @@ CREATE TABLE IF NOT EXISTS aisoc_attack_chains (
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
seed_alert_id UUID NOT NULL,
case_id UUID,
window VARCHAR(8) NOT NULL,
-- "window" is a reserved keyword in Postgres; it must be quoted
-- everywhere it is used as an identifier (column def, constraints,
-- and in the application's INSERT/ON CONFLICT statements).
"window" VARCHAR(8) NOT NULL,
chain JSONB NOT NULL DEFAULT '[]'::jsonb,
entity_graph JSONB NOT NULL DEFAULT '{}'::jsonb,
chain_signature VARCHAR(64) NOT NULL,
confidence NUMERIC(5, 4) NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT aisoc_attack_chains_window_check
CHECK (window IN ('1h', '6h', '24h', '72h', '7d', '30d')),
CHECK ("window" IN ('1h', '6h', '24h', '72h', '7d', '30d')),
CONSTRAINT aisoc_attack_chains_unique_signature
UNIQUE (tenant_id, seed_alert_id, window, chain_signature)
UNIQUE (tenant_id, seed_alert_id, "window", chain_signature)
);

CREATE INDEX IF NOT EXISTS aisoc_attack_chains_seed_idx
Expand Down
5 changes: 4 additions & 1 deletion services/api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ readme = "README.md"
python = "^3.11"
fastapi = ">=0.111,<0.137"
uvicorn = { version = ">=0.29,<0.49", extras = ["standard"] }
pydantic = ">=2.7,<2.14"
# EmailStr (used in auth/passkeys/tenants endpoints) requires the optional
# email-validator package, which is only pulled in via the "email" extra.
# Without it the API crashes on import with "email-validator is not installed".
pydantic = { version = ">=2.7,<2.14", extras = ["email"] }
pydantic-settings = "^2.2.1"
sqlalchemy = { version = "^2.0.30", extras = ["asyncio"] }
asyncpg = ">=0.29.0,<0.32"
Expand Down
Loading