use crate::packs::{DestructivePattern, Pack, SafePattern};
use crate::{destructive_pattern, safe_pattern};
#[must_use]
pub fn create_pack() -> Pack {
Pack {
id: "email.ses".to_string(),
name: "AWS SES",
description: "Protects against destructive AWS Simple Email Service operations like \
identity deletion, template deletion, and configuration set removal.",
keywords: &["ses", "sesv2"],
safe_patterns: create_safe_patterns(),
destructive_patterns: create_destructive_patterns(),
keyword_matcher: None,
safe_regex_set: None,
safe_regex_set_is_complete: false,
}
}
fn create_safe_patterns() -> Vec<SafePattern> {
vec![
safe_pattern!("ses-list-identities", r"\baws\s+ses\s+list-identities\b"),
safe_pattern!("ses-list-templates", r"\baws\s+ses\s+list-templates\b"),
safe_pattern!(
"ses-list-configuration-sets",
r"\baws\s+ses\s+list-configuration-sets\b"
),
safe_pattern!(
"ses-list-receipt-rules",
r"\baws\s+ses\s+list-receipt-rules\b"
),
safe_pattern!(
"ses-list-receipt-rule-sets",
r"\baws\s+ses\s+list-receipt-rule-sets\b"
),
safe_pattern!(
"ses-get-identity-verification-attributes",
r"\baws\s+ses\s+get-identity-verification-attributes\b"
),
safe_pattern!(
"ses-get-identity-dkim-attributes",
r"\baws\s+ses\s+get-identity-dkim-attributes\b"
),
safe_pattern!(
"ses-get-identity-notification-attributes",
r"\baws\s+ses\s+get-identity-notification-attributes\b"
),
safe_pattern!("ses-get-template", r"\baws\s+ses\s+get-template\b"),
safe_pattern!(
"ses-describe-configuration-set",
r"\baws\s+ses\s+describe-configuration-set\b"
),
safe_pattern!(
"ses-describe-receipt-rule",
r"\baws\s+ses\s+describe-receipt-rule\b"
),
safe_pattern!(
"ses-describe-receipt-rule-set",
r"\baws\s+ses\s+describe-receipt-rule-set\b"
),
safe_pattern!("ses-get-send-quota", r"\baws\s+ses\s+get-send-quota\b"),
safe_pattern!(
"ses-get-send-statistics",
r"\baws\s+ses\s+get-send-statistics\b"
),
safe_pattern!(
"sesv2-list-email-identities",
r"\baws\s+sesv2\s+list-email-identities\b"
),
safe_pattern!(
"sesv2-list-email-templates",
r"\baws\s+sesv2\s+list-email-templates\b"
),
safe_pattern!(
"sesv2-list-configuration-sets",
r"\baws\s+sesv2\s+list-configuration-sets\b"
),
safe_pattern!(
"sesv2-list-contact-lists",
r"\baws\s+sesv2\s+list-contact-lists\b"
),
safe_pattern!(
"sesv2-list-dedicated-ip-pools",
r"\baws\s+sesv2\s+list-dedicated-ip-pools\b"
),
safe_pattern!(
"sesv2-get-email-identity",
r"\baws\s+sesv2\s+get-email-identity\b"
),
safe_pattern!(
"sesv2-get-email-template",
r"\baws\s+sesv2\s+get-email-template\b"
),
safe_pattern!(
"sesv2-get-configuration-set",
r"\baws\s+sesv2\s+get-configuration-set\b"
),
safe_pattern!(
"sesv2-get-contact-list",
r"\baws\s+sesv2\s+get-contact-list\b"
),
safe_pattern!(
"sesv2-get-dedicated-ip-pool",
r"\baws\s+sesv2\s+get-dedicated-ip-pool\b"
),
safe_pattern!("sesv2-get-account", r"\baws\s+sesv2\s+get-account\b"),
]
}
fn create_destructive_patterns() -> Vec<DestructivePattern> {
vec![
destructive_pattern!(
"ses-delete-identity",
r"\baws\s+ses\s+delete-identity\b",
"aws ses delete-identity removes a verified email identity.",
High,
"Deleting a verified identity prevents sending from that address or domain. \
Applications using this identity will fail to send emails. Re-verification \
requires DNS changes and propagation time.\n\n\
Safer alternatives:\n\
- aws ses list-identities: Review identities first\n\
- Check which applications use this identity\n\
- Create new identity before deleting old one"
),
destructive_pattern!(
"ses-delete-template",
r"\baws\s+ses\s+delete-template\b",
"aws ses delete-template removes an email template.",
Medium,
"Deleting a template breaks any applications that reference it. Emails using \
this template will fail to send until the template is recreated.\n\n\
Safer alternatives:\n\
- aws ses get-template: Export template before deletion\n\
- Verify no active campaigns use this template\n\
- Create replacement template before deleting"
),
destructive_pattern!(
"ses-delete-configuration-set",
r"\baws\s+ses\s+delete-configuration-set\b",
"aws ses delete-configuration-set removes a configuration set.",
High,
"Deleting a configuration set removes tracking and event destinations. \
Applications using this set will lose metrics, bounce handling, and \
complaint processing.\n\n\
Safer alternatives:\n\
- aws ses describe-configuration-set: Review configuration\n\
- Migrate applications to a new configuration set first\n\
- Document event destinations before deletion"
),
destructive_pattern!(
"ses-delete-receipt-rule-set",
r"\baws\s+ses\s+delete-receipt-rule-set\b",
"aws ses delete-receipt-rule-set removes a receipt rule set.",
Critical,
"Deleting a receipt rule set stops all email receiving configured by that set. \
Incoming emails may bounce or be lost. This affects all receipt rules in the set.\n\n\
Safer alternatives:\n\
- aws ses describe-receipt-rule-set: Review rules first\n\
- Create replacement rule set before deletion\n\
- Test with a non-active rule set"
),
destructive_pattern!(
"ses-delete-receipt-rule",
r"\baws\s+ses\s+delete-receipt-rule(?:\s|$)",
"aws ses delete-receipt-rule removes a receipt rule.",
High,
"Deleting a receipt rule changes how incoming emails are processed. Actions \
like S3 storage, Lambda triggers, or SNS notifications will stop for \
matching emails.\n\n\
Safer alternatives:\n\
- aws ses describe-receipt-rule: Review rule configuration\n\
- Disable the rule before deleting\n\
- Ensure no critical workflows depend on this rule"
),
destructive_pattern!(
"sesv2-delete-email-identity",
r"\baws\s+sesv2\s+delete-email-identity\b",
"aws sesv2 delete-email-identity removes a verified email identity.",
High,
"Deleting a verified identity prevents sending from that address or domain. \
DKIM and SPF records become orphaned. Applications will fail until a new \
identity is verified.\n\n\
Safer alternatives:\n\
- aws sesv2 get-email-identity: Review identity configuration\n\
- Verify replacement identity before deletion\n\
- Update applications to use new identity first"
),
destructive_pattern!(
"sesv2-delete-email-template",
r"\baws\s+sesv2\s+delete-email-template\b",
"aws sesv2 delete-email-template removes an email template.",
Medium,
"Deleting a template breaks any send operations referencing it. Bulk email \
sends and transactional emails using this template will fail.\n\n\
Safer alternatives:\n\
- aws sesv2 get-email-template: Export template content\n\
- Check for active campaigns using this template\n\
- Version templates rather than deleting"
),
destructive_pattern!(
"sesv2-delete-configuration-set",
r"\baws\s+sesv2\s+delete-configuration-set\b",
"aws sesv2 delete-configuration-set removes a configuration set.",
High,
"Deleting a configuration set removes all event destinations, tracking options, \
and delivery settings. Applications using this set lose visibility into \
email delivery.\n\n\
Safer alternatives:\n\
- aws sesv2 get-configuration-set: Export configuration\n\
- Migrate to new configuration set first\n\
- Document all event destinations"
),
destructive_pattern!(
"sesv2-delete-contact-list",
r"\baws\s+sesv2\s+delete-contact-list\b",
"aws sesv2 delete-contact-list removes a contact list.",
High,
"Deleting a contact list permanently removes all contacts and their preferences. \
Subscription management and list-based sending will fail. This cannot be undone.\n\n\
Safer alternatives:\n\
- Export contact list data before deletion\n\
- Check for active campaigns using this list\n\
- Use list segmentation instead of deletion"
),
destructive_pattern!(
"sesv2-delete-dedicated-ip-pool",
r"\baws\s+sesv2\s+delete-dedicated-ip-pool\b",
"aws sesv2 delete-dedicated-ip-pool removes a dedicated IP pool.",
Critical,
"Deleting a dedicated IP pool releases the IPs back to the shared pool. \
Email reputation built on these IPs is lost. Configuration sets using \
this pool will fall back to shared IPs.\n\n\
Safer alternatives:\n\
- Migrate configuration sets to a new pool first\n\
- Document IP addresses and reputation metrics\n\
- Contact AWS support if IPs need to be preserved"
),
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packs::test_helpers::*;
#[test]
fn test_pack_creation() {
let pack = create_pack();
assert_eq!(pack.id, "email.ses");
assert_eq!(pack.name, "AWS SES");
assert!(!pack.description.is_empty());
assert!(pack.keywords.contains(&"ses"));
assert!(pack.keywords.contains(&"sesv2"));
assert_patterns_compile(&pack);
assert_all_patterns_have_reasons(&pack);
assert_unique_pattern_names(&pack);
}
#[test]
fn allows_safe_commands() {
let pack = create_pack();
assert_safe_pattern_matches(&pack, "aws ses list-identities");
assert_safe_pattern_matches(&pack, "aws ses list-templates");
assert_safe_pattern_matches(&pack, "aws ses list-configuration-sets");
assert_safe_pattern_matches(
&pack,
"aws ses list-receipt-rules --rule-set-name MyRuleSet",
);
assert_safe_pattern_matches(&pack, "aws ses list-receipt-rule-sets");
assert_safe_pattern_matches(
&pack,
"aws ses get-identity-verification-attributes --identities example.com",
);
assert_safe_pattern_matches(
&pack,
"aws ses get-identity-dkim-attributes --identities example.com",
);
assert_safe_pattern_matches(&pack, "aws ses get-template --template-name MyTemplate");
assert_safe_pattern_matches(
&pack,
"aws ses describe-configuration-set --configuration-set-name MySet",
);
assert_safe_pattern_matches(&pack, "aws ses get-send-quota");
assert_safe_pattern_matches(&pack, "aws ses get-send-statistics");
assert_safe_pattern_matches(&pack, "aws sesv2 list-email-identities");
assert_safe_pattern_matches(&pack, "aws sesv2 list-email-templates");
assert_safe_pattern_matches(&pack, "aws sesv2 list-configuration-sets");
assert_safe_pattern_matches(&pack, "aws sesv2 list-contact-lists");
assert_safe_pattern_matches(&pack, "aws sesv2 list-dedicated-ip-pools");
assert_safe_pattern_matches(
&pack,
"aws sesv2 get-email-identity --email-identity example.com",
);
assert_safe_pattern_matches(
&pack,
"aws sesv2 get-email-template --template-name MyTemplate",
);
assert_safe_pattern_matches(
&pack,
"aws sesv2 get-configuration-set --configuration-set-name MySet",
);
assert_safe_pattern_matches(&pack, "aws sesv2 get-account");
}
#[test]
fn blocks_destructive_commands() {
let pack = create_pack();
assert_blocks_with_pattern(
&pack,
"aws ses delete-identity --identity example.com",
"ses-delete-identity",
);
assert_blocks_with_pattern(
&pack,
"aws ses delete-template --template-name MyTemplate",
"ses-delete-template",
);
assert_blocks_with_pattern(
&pack,
"aws ses delete-configuration-set --configuration-set-name MySet",
"ses-delete-configuration-set",
);
assert_blocks_with_pattern(
&pack,
"aws ses delete-receipt-rule --rule-set-name MyRuleSet --rule-name MyRule",
"ses-delete-receipt-rule",
);
assert_blocks_with_pattern(
&pack,
"aws ses delete-receipt-rule-set --rule-set-name MyRuleSet",
"ses-delete-receipt-rule-set",
);
assert_blocks_with_pattern(
&pack,
"aws sesv2 delete-email-identity --email-identity example.com",
"sesv2-delete-email-identity",
);
assert_blocks_with_pattern(
&pack,
"aws sesv2 delete-email-template --template-name MyTemplate",
"sesv2-delete-email-template",
);
assert_blocks_with_pattern(
&pack,
"aws sesv2 delete-configuration-set --configuration-set-name MySet",
"sesv2-delete-configuration-set",
);
assert_blocks_with_pattern(
&pack,
"aws sesv2 delete-contact-list --contact-list-name MyList",
"sesv2-delete-contact-list",
);
assert_blocks_with_pattern(
&pack,
"aws sesv2 delete-dedicated-ip-pool --pool-name MyPool",
"sesv2-delete-dedicated-ip-pool",
);
}
}