@@ -73,15 +73,8 @@ public void testSnapshotNameValidation()
7373 assertThatCode (() -> cfs .snapshot ("a_tag_1and_something2-more" )).doesNotThrowAnyException ();
7474 assertThatCode (() -> cfs .snapshot (repeat ('a' , SchemaConstants .FILENAME_LENGTH ))).doesNotThrowAnyException ();
7575
76- // AWS S3 "Safe characters" accepted by the relaxed allowlist:
77- // ! . * ' ( )
78- // See https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html#object-key-guidelines
76+ // Only alphanumerics, '-', '_' and '.' are accepted.
7977 assertThatCode (() -> cfs .snapshot ("snap.2026-05-20" )).doesNotThrowAnyException ();
80- assertThatCode (() -> cfs .snapshot ("important!" )).doesNotThrowAnyException ();
81- assertThatCode (() -> cfs .snapshot ("backup*" )).doesNotThrowAnyException ();
82- assertThatCode (() -> cfs .snapshot ("o'snap" )).doesNotThrowAnyException ();
83- assertThatCode (() -> cfs .snapshot ("snap(1)" )).doesNotThrowAnyException ();
84- assertThatCode (() -> cfs .snapshot ("!._-*'()" )).doesNotThrowAnyException ();
8578 // Dots embedded in a name are not traversal: with '/' excluded, "a..tag" is just a literal directory.
8679 assertThatCode (() -> cfs .snapshot ("a..tag" )).doesNotThrowAnyException ();
8780
@@ -91,12 +84,26 @@ public void testSnapshotNameValidation()
9184 .hasMessage ("Snapshot name must not be more than 255 characters long for " +
9285 "resolved snapshot name (got 256 characters for \" " + tooLong + "\" )" );
9386
94- // '/' is not in the S3-safe set; this is what kills traversal attempts like "../../mysnapshot".
87+ // '/' is not in the allowed set; this is what kills traversal attempts like "../../mysnapshot".
9588 assertThatThrownBy (() -> cfs .snapshot ("a" + sep + "tag" ))
9689 .isInstanceOf (IllegalArgumentException .class )
9790 .hasMessage ("Snapshot name cannot contain " + sep );
9891
99- // Other characters outside the S3-safe set must still be rejected.
92+ // The shell-significant S3 "safe" characters (! * ' ( )) are deliberately NOT allowed.
93+ assertThatThrownBy (() -> cfs .snapshot ("important!" ))
94+ .isInstanceOf (IllegalArgumentException .class )
95+ .hasMessage ("Snapshot name contains illegal characters: important!" );
96+ assertThatThrownBy (() -> cfs .snapshot ("backup*" ))
97+ .isInstanceOf (IllegalArgumentException .class )
98+ .hasMessage ("Snapshot name contains illegal characters: backup*" );
99+ assertThatThrownBy (() -> cfs .snapshot ("o'snap" ))
100+ .isInstanceOf (IllegalArgumentException .class )
101+ .hasMessage ("Snapshot name contains illegal characters: o'snap" );
102+ assertThatThrownBy (() -> cfs .snapshot ("snap(1)" ))
103+ .isInstanceOf (IllegalArgumentException .class )
104+ .hasMessage ("Snapshot name contains illegal characters: snap(1)" );
105+
106+ // Other characters outside the allowed set must still be rejected.
100107 assertThatThrownBy (() -> cfs .snapshot ("a tag" ))
101108 .isInstanceOf (IllegalArgumentException .class )
102109 .hasMessage ("Snapshot name contains illegal characters: a tag" );
@@ -118,9 +125,12 @@ public void testSnapshotNameValidation()
118125 {
119126 p .set (CassandraRelevantProperties .SNAPSHOT_NAME_VALIDATION , false );
120127
121- // Previously-rejected characters are now accepted: space, ':', and other non-S3-safe chars.
128+ // The character check is bypassed entirely: space, ':', and the now-disallowed
129+ // shell-significant characters (! * ' ( )) are all accepted.
122130 assertThatCode (() -> cfs .snapshot ("a tag" )).doesNotThrowAnyException ();
123131 assertThatCode (() -> cfs .snapshot ("a:tag" )).doesNotThrowAnyException ();
132+ assertThatCode (() -> cfs .snapshot ("important!" )).doesNotThrowAnyException ();
133+ assertThatCode (() -> cfs .snapshot ("snap(1)" )).doesNotThrowAnyException ();
124134
125135 // Path separator and "." / ".." rejections are unconditional — they guard against
126136 // traversal regardless of the toggle.
0 commit comments