call_cedar_macro_as_argument
A Cedar macro call passed as an argument to another macro call:
semverGT(semver(2, 1, 1), semver(2, 1, 0)). The inner semver(...) calls
expand into record literals first, then splice into the outer semverGT(...)
call, which expands into the nested if/comparison. The result is a
constant-true guard (2.1.1 > 2.1.0), so the permit fires for every
Drupe::Action::"GetStockInfo" request.
The two macros (semver, semverGT) are lifted verbatim from macros corpus
0036_semver_rfc0061 (Cedar RFC-0061, translated to Dogwood) and live in
macros.dw, passed via --macros.
Validate (run from this directory):
dogwood validate policy.dw --policy-schema schema.cedarschema --macros macros.dw
Referenced by guide/09-calling-macros.md.
Policy
// A macro call passed as an ARGUMENT to another macro call: the inner
// semver(...) records are expanded first, then spliced into semverGT.
// (RFC-0061 semantic-versioning macros, translated to Dogwood.)
@id("gate_on_semver_compare")
permit (principal, action == Drupe::Action::"GetStockInfo", resource)
when {
semverGT(semver(2, 1, 1), semver(2, 1, 0))
};
Schema
namespace Drupe {
type ApproveSaleInput = {
shares: Long,
stock: String
};
type ApproveSaleOutput = {
approved: Bool
};
type ContentFilterFinding = {
severityScore: decimal
};
type GetStockInfoInput = {
stock: String
};
type GetStockInfoOutput = {
info: String
};
type PromptAttackFinding = {
severityScore: decimal
};
type SellSharesInput = {
shares: Long,
stock: String
};
type SellSharesOutput = {
proceeds: decimal
};
type SensitiveInfoFinding = {
confidenceScore: decimal
};
type SystemContext = {
now: datetime
};
entity Gateway;
entity IamEntity = {
id: String
};
entity OAuthUser = {
id: String
} tags String;
entity UnauthenticatedUser;
action "ApproveSale" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: ApproveSaleInput,
output?: ApproveSaleOutput,
system: SystemContext
}
};
action "CallTool" in [Action::"Mcp"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "GetStockInfo" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: GetStockInfoInput,
output?: GetStockInfoOutput,
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 "Mcp" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "SellShares" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: SellSharesInput,
output?: SellSharesOutput,
system: SystemContext
}
};
action "UnknownTool" in [Action::"CallTool"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
}
Macros
// RFC-0061 semantic-versioning macros (lifted verbatim from macros corpus
// 0036_semver_rfc0061/policy_1.dw): a record-building macro `semver` and a
// comparing macro `semverGT`. `semver(...)` may be passed as an argument to
// `semverGT(...)` — the inner calls expand first, then splice in.
def cedar semver(?major, ?minor, ?patch) {
{ major: ?major, minor: ?minor, patch: ?patch }
};
def cedar semverGT(?lhs, ?rhs) {
if ?lhs.major == ?rhs.major then
if ?lhs.minor == ?rhs.minor then
?lhs.patch > ?rhs.patch
else
?lhs.minor > ?rhs.minor
else
?lhs.major > ?rhs.major
};