From f363822cc7cb01f02a9bcb3f635c034713eb165c Mon Sep 17 00:00:00 2001 From: Sungkyu Yoo Date: Sun, 19 Apr 2026 22:33:14 +0900 Subject: [PATCH 1/2] Potential fix for code scanning alert no. 3: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- internal/services/ecr/store.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/services/ecr/store.go b/internal/services/ecr/store.go index bffca9e..9701ce6 100644 --- a/internal/services/ecr/store.go +++ b/internal/services/ecr/store.go @@ -387,6 +387,15 @@ func (s *ECRStore) InitiateLayerUpload(accountID, repoName string) (string, erro // UploadLayerPart saves layer part blob to the filesystem and records the part metadata. func (s *ECRStore) UploadLayerPart(accountID, repoName, uploadID string, partFirst, partLast int64, blob []byte) error { + // uploadID is used as a filesystem path component; enforce generated-ID format. + // InitiateLayerUpload creates 16 random bytes encoded as 32 lowercase hex chars. + if len(uploadID) != 32 { + return ErrLayerUploadNotFound + } + if _, err := hex.DecodeString(uploadID); err != nil { + return ErrLayerUploadNotFound + } + // Verify upload exists. var exists int _ = s.db().QueryRow(`SELECT COUNT(*) FROM layers WHERE upload_id=? AND repo_name=? AND account_id=?`, uploadID, repoName, accountID).Scan(&exists) From 75cccf8d255ec974bc447211a4128c2fef2e7c56 Mon Sep 17 00:00:00 2001 From: Sung-Kyu Yoo Date: Mon, 20 Apr 2026 03:54:27 +0900 Subject: [PATCH 2/2] fix: enforce lowercase hex in ECR upload ID validation hex.DecodeString accepts both upper- and lowercase, but uploadIDs are generated as 32 lowercase hex chars. Add an explicit check to reject uppercase input. --- internal/services/ecr/store.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/services/ecr/store.go b/internal/services/ecr/store.go index 9701ce6..999e599 100644 --- a/internal/services/ecr/store.go +++ b/internal/services/ecr/store.go @@ -12,6 +12,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "time" "github.com/skyoo2003/devcloud/internal/storage/sqlite" @@ -389,7 +390,7 @@ func (s *ECRStore) InitiateLayerUpload(accountID, repoName string) (string, erro func (s *ECRStore) UploadLayerPart(accountID, repoName, uploadID string, partFirst, partLast int64, blob []byte) error { // uploadID is used as a filesystem path component; enforce generated-ID format. // InitiateLayerUpload creates 16 random bytes encoded as 32 lowercase hex chars. - if len(uploadID) != 32 { + if len(uploadID) != 32 || uploadID != strings.ToLower(uploadID) { return ErrLayerUploadNotFound } if _, err := hex.DecodeString(uploadID); err != nil {