Skip to content

Commit 3b0d05d

Browse files
committed
admin: reject slash-bearing table names at create time (Codex P2)
handleDescribe and handleDelete already 404 on names containing '/', so a user could create `foo/bar` and then never be able to manage it through the admin surface — orphaned tables would only be reachable via the SigV4 path, which is exactly the asymmetry Codex flagged. validateCreateTableRequest now rejects '/' before the request reaches the source. Tests cover single-slash and multi-slash names alongside the existing trailing-JSON / unknown-field cases.
1 parent 4c4956e commit 3b0d05d

2 files changed

Lines changed: 12 additions & 1 deletion

File tree

internal/admin/dynamo_handler.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,16 @@ func validateCreateTableRequest(in *CreateTableRequest) error {
383383
if strings.TrimSpace(in.TableName) == "" {
384384
return errors.New("table_name is required")
385385
}
386+
// Reject slash-bearing names symmetrically with handleDescribe
387+
// and handleDelete, which already 404 on `/`. Without this
388+
// guard a user could create `foo/bar` and then never be able
389+
// to describe or delete it through the same admin surface —
390+
// the orphaned table would be reachable only through the SigV4
391+
// path. Blocking the asymmetric edge case at create time is
392+
// strictly better than discovering it later.
393+
if strings.ContainsRune(in.TableName, '/') {
394+
return errors.New("table_name must not contain '/'")
395+
}
386396
if err := validateAttribute(in.PartitionKey, "partition_key"); err != nil {
387397
return err
388398
}

internal/admin/dynamo_handler_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ func (s *stubTablesSource) AdminDeleteTable(_ context.Context, principal AuthPri
9393
return nil
9494
}
9595

96-
9796
func newDynamoHandlerForTest(src TablesSource) *DynamoHandler {
9897
return NewDynamoHandler(src)
9998
}
@@ -440,6 +439,8 @@ func TestDynamoHandler_CreateTable_RejectsBadJSON(t *testing.T) {
440439
`{"table_name":"u","partition_key":{"name":"id","type":"S"},"unknown_field":1}`, // strict decode
441440
`{"table_name":"u","partition_key":{"name":"id","type":"S"}}{"second":"object"}`, // trailing JSON
442441
`{"table_name":"u","partition_key":{"name":"id","type":"S"}} 42`, // trailing scalar
442+
`{"table_name":"foo/bar","partition_key":{"name":"id","type":"S"}}`, // slash in name
443+
`{"table_name":"a/b/c","partition_key":{"name":"id","type":"S"}}`, // multiple slashes
443444
}
444445
for _, body := range cases {
445446
t.Run(body, func(t *testing.T) {

0 commit comments

Comments
 (0)