@@ -22,13 +22,15 @@ public function __construct(
2222 protected ?int $ revision = 0 ,
2323 protected ?string $ path = null ,
2424 protected ?AbstractVault $ vault = null ,
25+ protected bool $ skipValidation = false ,
2526 ) {
26- $ this ->key = $ this ->validateKey ($ key );
27+ $ this ->key = $ skipValidation ? trim ( $ key ) : $ this ->validateKey ($ key );
2728 }
2829
2930 /**
30- * Validate a secret key using strict whitelist validation.
31- * Only allows letters, digits, and underscores (standard .env format).
31+ * Validate a secret key for safe vault operations.
32+ * Allows common naming conventions (letters, digits, underscores, hyphens)
33+ * while preventing characters that could break vault API calls.
3234 *
3335 * @param string $key The raw key to validate
3436 * @return string The validated key
@@ -39,11 +41,11 @@ protected function validateKey(string $key): string
3941 {
4042 $ trimmed = trim ($ key );
4143
42- // Strict whitelist: Only allow letters, digits, and underscores
43- if (! preg_match ('/^[A-Za-z0-9_]+$/ ' , $ trimmed )) {
44+ // Allow letters, digits, underscores, and hyphens (common in cloud services)
45+ if (! preg_match ('/^[A-Za-z0-9_- ]+$/ ' , $ trimmed )) {
4446 throw new \InvalidArgumentException (
4547 "Secret key ' {$ key }' contains invalid characters. " .
46- 'Only letters, numbers, and underscores are allowed. '
48+ 'Only letters, numbers, underscores, and hyphens are allowed. '
4749 );
4850 }
4951
@@ -54,28 +56,103 @@ protected function validateKey(string $key): string
5456 );
5557 }
5658
57- // Cannot start with underscore (poor practice, could conflict with system vars )
58- if (str_starts_with ($ trimmed , '_ ' )) {
59+ // Cannot start with hyphen ( could be interpreted as command flag )
60+ if (str_starts_with ($ trimmed , '- ' )) {
5961 throw new \InvalidArgumentException (
60- "Secret key ' {$ key }' cannot start with underscore. "
61- );
62- }
63-
64- // Cannot start with digit (invalid variable name in most languages)
65- if (preg_match ('/^[0-9]/ ' , $ trimmed )) {
66- throw new \InvalidArgumentException (
67- "Secret key ' {$ key }' cannot start with a number. "
62+ "Secret key ' {$ key }' cannot start with hyphen. "
6863 );
6964 }
7065
7166 return $ trimmed ;
7267 }
7368
69+ /**
70+ * Create a Secret from vault data (permissive key validation).
71+ * Use this when reading existing secrets from external sources.
72+ */
73+ public static function fromVault (
74+ string $ key ,
75+ ?string $ value = null ,
76+ ?string $ encryptedValue = null ,
77+ bool $ secure = true ,
78+ ?string $ stage = null ,
79+ ?int $ revision = 0 ,
80+ ?string $ path = null ,
81+ ?AbstractVault $ vault = null ,
82+ ): static {
83+ return new static (
84+ key: $ key ,
85+ value: $ value ,
86+ encryptedValue: $ encryptedValue ,
87+ secure: $ secure ,
88+ stage: $ stage ,
89+ revision: $ revision ,
90+ path: $ path ,
91+ vault: $ vault ,
92+ skipValidation: true ,
93+ );
94+ }
95+
96+ /**
97+ * Create a Secret from user input (strict key validation).
98+ * Use this when accepting user-provided secret names.
99+ */
100+ public static function fromUser (
101+ string $ key ,
102+ ?string $ value = null ,
103+ ?string $ encryptedValue = null ,
104+ bool $ secure = true ,
105+ ?string $ stage = null ,
106+ ?int $ revision = 0 ,
107+ ?string $ path = null ,
108+ ?AbstractVault $ vault = null ,
109+ ): static {
110+ return new static (
111+ key: $ key ,
112+ value: $ value ,
113+ encryptedValue: $ encryptedValue ,
114+ secure: $ secure ,
115+ stage: $ stage ,
116+ revision: $ revision ,
117+ path: $ path ,
118+ vault: $ vault ,
119+ skipValidation: false ,
120+ );
121+ }
122+
74123 public function key ()
75124 {
76125 return $ this ->key ;
77126 }
78127
128+ /**
129+ * Get a sanitized version of the key safe for .env files.
130+ * Converts non-alphanumeric characters to underscores and ensures valid .env format.
131+ */
132+ public function sanitizedKey (): string
133+ {
134+ $ sanitized = $ this ->key ;
135+
136+ // Replace invalid characters with underscores
137+ $ sanitized = preg_replace ('/[^A-Za-z0-9_]/ ' , '_ ' , $ sanitized );
138+
139+ // Remove leading underscores
140+ $ sanitized = ltrim ($ sanitized , '_ ' );
141+
142+ // Remove leading digits by prefixing with 'KEY_'
143+ if (preg_match ('/^[0-9]/ ' , $ sanitized )) {
144+ $ sanitized = 'KEY_ ' . $ sanitized ;
145+ }
146+
147+ // Handle empty string case
148+ if (empty ($ sanitized )) {
149+ $ sanitized = 'UNNAMED_KEY ' ;
150+ }
151+
152+ // Convert to uppercase (common .env convention)
153+ return strtoupper ($ sanitized );
154+ }
155+
79156 public function value (): ?string
80157 {
81158 return $ this ->value ;
0 commit comments