agentguard/commands/
init.rs1use agentguard_core::{init_store, PolicyStore};
2use anyhow::{anyhow, Result};
3use std::path::Path;
4
5fn sanitize_org_name(name: &str) -> Result<&str> {
11 if name.is_empty() {
12 return Err(anyhow!("--name cannot be empty"));
13 }
14 if name.len() > 64 {
15 return Err(anyhow!("--name is too long (max 64 chars)"));
16 }
17 for c in name.chars() {
18 let ok = c.is_ascii_alphanumeric()
19 || matches!(c, '-' | '_' | '.' | ' ')
20 || c.is_ascii_punctuation() && c != '"' && c != '\\';
21 if !ok {
22 return Err(anyhow!(
23 "--name contains an unsafe character {:?}; allowed: ASCII alphanumeric, '-', '_', '.', ' ', and basic punctuation except quotes and backslashes",
24 c
25 ));
26 }
27 }
28 Ok(name)
29}
30
31pub fn run(store: impl AsRef<Path>, name: &str) -> Result<()> {
32 let path = store.as_ref();
33 let name = sanitize_org_name(name)?;
34 init_store(path)?;
35 let store = PolicyStore::open(path)?;
36
37 eprintln!(
42 "warning: `10_admin.cedar` grants `User::\"admin\"` superuser privileges. \
43 Replace this file before deploying to production."
44 );
45 store.write_policy(
46 "10_admin",
47 &format!(
48 r#"// Auto-generated by `agentguard init` for org `{name}`.
49// SECURITY: This policy grants the `User::"admin"` principal ANY
50// action on ANY resource. It is a development default only;
51// replace this file before deploying to production.
52permit (
53 principal in User::"admin",
54 action,
55 resource
56);
57"#
58 ),
59 )?;
60
61 eprintln!(
63 "warning: `20_agents.cedar` permits any `Agent` to invoke any declared \
64 ToolCall action. Tighten with `when {{ ... }}` clauses before deploying."
65 );
66 store.write_policy(
67 "20_agents",
68 r#"// AI agents can invoke any declared ToolCall action.
69// SECURITY: This is a development default that grants every agent
70// unrestricted tool access. Tighten with `when { ... }` clauses
71// before deploying.
72permit (
73 principal is Agent,
74 action,
75 resource
76);
77"#,
78 )?;
79
80 println!("Initialized agentguard store at {}", store.root.display());
81 println!(" schema: {}", store.schema_path().display());
82 println!(" policies: {}", store.policies_dir().display());
83 println!("Try:");
84 println!(" agentguard validate");
85 Ok(())
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn sanitize_allows_safe_names() {
94 assert!(sanitize_org_name("acme-corp").is_ok());
95 assert!(sanitize_org_name("Acme Corp.").is_ok());
96 assert!(sanitize_org_name("acme_corp.io").is_ok());
97 }
98
99 #[test]
100 fn sanitize_rejects_unsafe() {
101 assert!(sanitize_org_name("").is_err());
102 assert!(sanitize_org_name(&"x".repeat(65)).is_err());
103 assert!(sanitize_org_name(r#"acme"; permit (any); "#).is_err());
104 assert!(sanitize_org_name("acme\\inject").is_err());
105 }
106}