alert_login_in_last_hour
Counting over history: a formerly within 1h body inside the aggregation
makes the count range over the whole window, not just the current timepoint.
Permit an Alert if at least one Login to this server
(input.server: context.input.server) occurred in the last hour. The
exists (n: Long). ((count …) == n && n > 0) idiom just asserts the historical
count is positive.
The trace shows both a fire and a non-fire:
@0—Logintos1by alice (a history-only event here; noAlertpermit applies, so the decision is a deny).@100— alice raises anAlertfor servers1, with a matchingLogintos1within the window → allow.@200— alice raises anAlertfor servers2, with noLogintos2→ deny.
Referenced by guide/04-temporal-expressions.md.
Policy
// Counting over HISTORY: a `formerly within 1h` body inside the aggregation
// makes the count range over the whole window, not just the current
// timepoint. Permit an Alert if at least one Login to this server occurred in
// the last hour.
@id("alert_login_in_last_hour")
permit (
principal,
action == Drupe::Action::"Alert",
resource
)
when temporal {
exists (n: Long). (
(count for (t: Timepoint). where (
formerly within 1h (Drupe::Action::"Login"::request{ input.user: _, input.server: context.input.server } && tp(t))
)) == n && n > 0
)
};
Schema
namespace Drupe {
type AlertInput = {
level: Long,
server: String
};
type AlertOutput = { };
type ContentFilterFinding = {
severityScore: decimal
};
type LoginInput = {
server: String,
user: String
};
type LoginOutput = { };
type PromptAttackFinding = {
severityScore: decimal
};
type SensitiveInfoFinding = {
confidenceScore: decimal
};
type SystemContext = {
now: datetime
};
entity Gateway;
entity IamEntity = {
id: String
};
entity OAuthUser = {
id: String
} tags String;
entity UnauthenticatedUser;
action "Alert" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: AlertInput,
output?: AlertOutput,
system: SystemContext
}
};
action "CallTool" in [Action::"Mcp"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "Http" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "InvokeAgent" in [Action::"Http"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
input?: { },
system: SystemContext
}
};
action "InvokeLLM" in [Action::"Http"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
input?: { },
system: SystemContext
}
};
action "Login" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: LoginInput,
output?: LoginOutput,
system: SystemContext
}
};
action "Mcp" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "UnknownTool" in [Action::"CallTool"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
}
Trace
@0 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { server: "s1", user: "alice" }) Drupe::Action::"Login"::request(input: { server: "s1", user: "alice" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u1")
@100 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { level: 1, server: "s1" }) Drupe::Action::"Alert"::request(input: { level: 1, server: "s1" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u2")
@200 scope(principal: Drupe::OAuthUser::"alice", resource: Drupe::Gateway::"gw1") request_context(input: { level: 2, server: "s2" }) Drupe::Action::"Alert"::request(input: { level: 2, server: "s2" }, callerPrincipal: Drupe::OAuthUser::"alice", callerResource: Drupe::Gateway::"gw1", requestId: "u3")
Expected Output
@0 (time point 0): DENY
@100 (time point 1): ALLOW [rules: 0]
@200 (time point 2): DENY