use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CaptureSemanticRole {
#[default]
Unknown,
AssignmentValue,
Token,
CredentialEnvelope,
PrivateKeyBlock,
ConnectionString,
UrlUserinfo,
HeaderValue,
CommandArgumentValue,
}
impl CaptureSemanticRole {
pub const fn as_str(self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::AssignmentValue => "assignment-value",
Self::Token => "token",
Self::CredentialEnvelope => "credential-envelope",
Self::PrivateKeyBlock => "private-key-block",
Self::ConnectionString => "connection-string",
Self::UrlUserinfo => "url-userinfo",
Self::HeaderValue => "header-value",
Self::CommandArgumentValue => "command-argument-value",
}
}
pub const fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AnchorSemanticRole {
#[default]
Unknown,
ExactKey,
DistinctivePrefix,
StructuredEnvelope,
CompanionBound,
WeakContext,
Unanchored,
}
impl AnchorSemanticRole {
pub const fn as_str(self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::ExactKey => "exact-key",
Self::DistinctivePrefix => "distinctive-prefix",
Self::StructuredEnvelope => "structured-envelope",
Self::CompanionBound => "companion-bound",
Self::WeakContext => "weak-context",
Self::Unanchored => "unanchored",
}
}
pub const fn is_unknown(&self) -> bool {
matches!(self, Self::Unknown)
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum SemanticSourceRole {
StructuredAssignmentValue,
EnvironmentAssignmentValue,
StringLiteral,
CommandArgumentValue,
CommandOptionDeclaration,
HeaderValue,
UrlAuthorityUserinfo,
ConnectionString,
StandaloneToken,
PemBlock,
RegexRuleDefinition,
IdentifierTypeMemberName,
ProseDocumentation,
TestFixture,
GeneratedVendorMaterial,
Unknown,
}
impl SemanticSourceRole {
pub const fn as_str(self) -> &'static str {
match self {
Self::StructuredAssignmentValue => "structured-assignment-value",
Self::EnvironmentAssignmentValue => "environment-assignment-value",
Self::StringLiteral => "string-literal",
Self::CommandArgumentValue => "command-argument-value",
Self::CommandOptionDeclaration => "command-option-declaration",
Self::HeaderValue => "header-value",
Self::UrlAuthorityUserinfo => "url-authority-userinfo",
Self::ConnectionString => "connection-string",
Self::StandaloneToken => "standalone-token",
Self::PemBlock => "pem-block",
Self::RegexRuleDefinition => "regex-rule-definition",
Self::IdentifierTypeMemberName => "identifier-type-member-name",
Self::ProseDocumentation => "prose-documentation",
Self::TestFixture => "test-fixture",
Self::GeneratedVendorMaterial => "generated-vendor-material",
Self::Unknown => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum RequiredSemanticEvidence {
Checksum,
RequiredCompanion,
PrivateKeyCompanion,
StructuralGrammar,
LiveVerification,
}
impl RequiredSemanticEvidence {
pub const fn as_str(self) -> &'static str {
match self {
Self::Checksum => "checksum",
Self::RequiredCompanion => "required-companion",
Self::PrivateKeyCompanion => "private-key-companion",
Self::StructuralGrammar => "structural-grammar",
Self::LiveVerification => "live-verification",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum DetectorHardNegativeClass {
Boundary,
Identifier,
Prose,
RegexLiteral,
SiblingPrefix,
}
impl DetectorHardNegativeClass {
pub const ALL: &'static [Self] = &[
Self::Boundary,
Self::Identifier,
Self::Prose,
Self::RegexLiteral,
Self::SiblingPrefix,
];
pub const fn as_str(self) -> &'static str {
match self {
Self::Boundary => "boundary",
Self::Identifier => "identifier",
Self::Prose => "prose",
Self::RegexLiteral => "regex-literal",
Self::SiblingPrefix => "sibling-prefix",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DetectorSemanticPolicySpec {
#[serde(default)]
pub capture_role: CaptureSemanticRole,
#[serde(default)]
pub anchor_role: AnchorSemanticRole,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub allowed_source_roles: Vec<SemanticSourceRole>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_evidence: Vec<RequiredSemanticEvidence>,
}
impl DetectorSemanticPolicySpec {
pub fn is_enforcement_capable(&self) -> bool {
self.capture_role != CaptureSemanticRole::Unknown
&& self.anchor_role != AnchorSemanticRole::Unknown
&& !self.allowed_source_roles.is_empty()
&& self
.allowed_source_roles
.iter()
.all(|role| *role != SemanticSourceRole::Unknown)
}
}