Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions .github/workflows/integration-per-language.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ jobs:
--skip-file-detection \
--deploy-type helm \
--variable PORT=8080 \
--variable APPNAME=testingCreateCommand \
--variable APPNAME=testing-create-command \
--variable VERSION=1.11 \
--variable BUILDERVERSION=1.11 \
--variable SERVICEPORT=8080 \
--variable NAMESPACE=testNamespace \
--variable IMAGENAME=testImage \
--variable NAMESPACE=test-namespace \
--variable IMAGENAME=testimage \
--variable IMAGETAG=latest \
--variable ENTRYPOINT=myapp.py
- name: Validate JSON
Expand Down Expand Up @@ -150,7 +150,7 @@ jobs:
--variable BRANCHNAME=main \
--variable ACRRESOURCEGROUP=someAcrResourceGroup \
--variable AZURECONTAINERREGISTRY=someRegistry \
--variable CONTAINERNAME=someContainer \
--variable CONTAINERNAME=somecontainer \
--variable CLUSTERRESOURCEGROUP=someClusterResourceGroup \
--variable CLUSTERNAME=someAksCluster \
--variable DOCKERFILE=./Dockerfile \
Expand Down Expand Up @@ -216,12 +216,12 @@ jobs:
--skip-file-detection \
--deploy-type kustomize \
--variable PORT=8080 \
--variable APPNAME=testingCreateCommand \
--variable APPNAME=testing-create-command \
--variable VERSION=1.11 \
--variable BUILDERVERSION=1.11 \
--variable SERVICEPORT=8080 \
--variable NAMESPACE=testNamespace \
--variable IMAGENAME=testImage \
--variable NAMESPACE=test-namespace \
--variable IMAGENAME=testimage \
--variable IMAGETAG=latest \
--variable ENTRYPOINT=myapp.py
- name: Validate JSON
Expand Down Expand Up @@ -315,7 +315,7 @@ jobs:
--variable BRANCHNAME=main \
--variable ACRRESOURCEGROUP=someAcrResourceGroup \
--variable AZURECONTAINERREGISTRY=someRegistry \
--variable CONTAINERNAME=someContainer \
--variable CONTAINERNAME=somecontainer \
--variable CLUSTERRESOURCEGROUP=someClusterResourceGroup \
--variable CLUSTERNAME=someAksCluster \
--variable DOCKERFILE=./Dockerfile \
Expand Down Expand Up @@ -380,12 +380,12 @@ jobs:
--skip-file-detection \
--deploy-type manifests \
--variable PORT=8080 \
--variable APPNAME=testingCreateCommand \
--variable APPNAME=testing-create-command \
--variable VERSION=1.11 \
--variable BUILDERVERSION=1.11 \
--variable SERVICEPORT=80 \
--variable NAMESPACE=testNamespace \
--variable IMAGENAME=testImage \
--variable NAMESPACE=test-namespace \
--variable IMAGENAME=testimage \
--variable IMAGETAG=latest \
--variable ENTRYPOINT=myapp.py
- name: Validate JSON
Expand Down Expand Up @@ -472,7 +472,7 @@ jobs:
--variable BRANCHNAME=main \
--variable ACRRESOURCEGROUP=someAcrResourceGroup \
--variable AZURECONTAINERREGISTRY=someRegistry \
--variable CONTAINERNAME=someContainer \
--variable CONTAINERNAME=somecontainer \
--variable CLUSTERRESOURCEGROUP=someClusterResourceGroup \
--variable CLUSTERNAME=someAksCluster \
--variable DOCKERFILE=./Dockerfile \
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## [0.17.16] - 2026-10-12

- Security: validate and encode template variables to prevent instruction injection in generated artifacts ([GHSA-xqmq-h7rh-g4cq](https://github.com/Azure/draft/security/advisories/GHSA-xqmq-h7rh-g4cq))

## [0.17.14] - 2026-01-08

- Dependabot updates for dependencies and actions
Expand Down
2 changes: 1 addition & 1 deletion cmd/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestRun(t *testing.T) {
{Name: "DOCKERFILENAME", Value: "Dockerfile"},
},
}
flagVariablesMap = map[string]string{"PORT": "8080", "APPNAME": "testing-create-command", "VERSION": "1.18", "SERVICEPORT": "8080", "NAMESPACE": "test-namespace", "IMAGENAME": "testImage", "IMAGETAG": "latest", "DOCKERFILENAME": "test.Dockerfile"}
flagVariablesMap = map[string]string{"PORT": "8080", "APPNAME": "testing-create-command", "VERSION": "1.18", "SERVICEPORT": "8080", "NAMESPACE": "test-namespace", "IMAGENAME": "host.minikube.internal:5001/testapp", "IMAGETAG": "latest", "DOCKERFILENAME": "test.Dockerfile"}
mockCC := createCmd{
deployType: deployType,
dest: testDir,
Expand Down
21 changes: 20 additions & 1 deletion pkg/config/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@ const (
maxImageTagLength = 128
// maxPort is the highest valid TCP/UDP port.
maxPort = 65535

// Docker reference name grammar (namePat), without tags or digests:
// https://github.com/distribution/reference/blob/main/regexp.go
imageDomainComponent = `(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`
imageHost = `(?:` + imageDomainComponent + `(?:\.` + imageDomainComponent + `)*|\[(?:[a-fA-F0-9:]+)\])`
imagePathComponent = `[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*`
imageName = `(?:` + imageHost + `(?::[0-9]+)?/)?` + imagePathComponent + `(?:/` + imagePathComponent + `)*`
)

var (
ciNamePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._/-]*$`)
containerImageNamePattern = regexp.MustCompile(`^` + imageName + `$`)
helmChartOverridesPattern = regexp.MustCompile(`^[A-Za-z0-9_.-]+=[A-Za-z0-9_.:/+-]+(?:,[A-Za-z0-9_.-]+=[A-Za-z0-9_.:/+-]+)*$`)
)

Expand All @@ -50,8 +58,10 @@ func safeNameRunes(r rune) bool {

func GetValidator(variableKind string) func(string) error {
switch variableKind {
case "azureContainerRegistry", "azureManagedCluster", "azureResourceGroup", "containerImageName":
case "azureContainerRegistry", "azureManagedCluster", "azureResourceGroup":
return ciNameValidator
case "containerImageName":
return containerImageNameValidator
case "containerImageVersion":
return containerImageVersionValidator
case "dirPath":
Expand Down Expand Up @@ -90,6 +100,15 @@ func ciNameValidator(input string) error {
return nil
}

// Image tags are supplied separately by containerImageVersion. Anchor Docker's
// name grammar so a valid substring cannot conceal trailing shell syntax.
func containerImageNameValidator(input string) error {
if !containerImageNamePattern.MatchString(input) {
return fmt.Errorf("invalid container image name %q: expected a repository name with an optional registry and port", input)
}
return nil
}

func kubernetesDNSLabelValidator(input string) error {
if errs := validation.IsDNS1123Label(input); len(errs) > 0 {
return fmt.Errorf("invalid Kubernetes name %q: %s", input, strings.Join(errs, "; "))
Expand Down
17 changes: 17 additions & 0 deletions pkg/config/validators/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,23 @@ func TestCINameValidatorsRejectShellSyntax(t *testing.T) {
assert.Nil(t, GetValidator("kubernetesNamespace")("team-a"))
}

func TestContainerImageNameValidator(t *testing.T) {
for _, input := range []string{
"testapp", "team/my_app", "mcr.microsoft.com/team/app",
"host.minikube.internal:5001/testapp", "localhost:5000/team/app",
"127.0.0.1:5000/app",
} {
assert.NoError(t, GetValidator("containerImageName")(input), "expected %q to be valid", input)
}
for _, input := range []string{
"", "app:latest", "app@sha256:abcd", "https://registry/app", "UPPERCASE",
"registry:port/app", "app;command", "app$(command)", "app --option value",
"app\nRUN command", `app"`, "app#comment",
} {
assert.Error(t, GetValidator("containerImageName")(input), "expected %q to be rejected", input)
}
}

func TestKubernetesResourceNameValidator(t *testing.T) {
for _, input := range []string{"app", "my-app", "app2"} {
assert.Nil(t, GetValidator("kubernetesResourceName")(input), "expected %q to be valid", input)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ on:
env:
ACR_RESOURCE_GROUP: testAcrRG
AZURE_CONTAINER_REGISTRY: testAcr
CONTAINER_NAME: testContainer
CONTAINER_NAME: testcontainer
CLUSTER_NAME: testCluster
CLUSTER_RESOURCE_GROUP: testClusterRG
CLUSTER_RESOURCE_TYPE: Microsoft.ContainerService/managedClusters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ on:
env:
ACR_RESOURCE_GROUP: testAcrRG
AZURE_CONTAINER_REGISTRY: testAcr
CONTAINER_NAME: testContainer
CONTAINER_NAME: testcontainer
CLUSTER_NAME: testCluster
CLUSTER_RESOURCE_GROUP: testClusterRG
CLUSTER_RESOURCE_TYPE: Microsoft.ContainerService/managedClusters
Expand Down
2 changes: 1 addition & 1 deletion pkg/handlers/templatetests/workflows_github_helm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestGitHubWorkflowHelmTemplates(t *testing.T) {
"BRANCHNAME": "testBranch",
"ACRRESOURCEGROUP": "testAcrRG",
"AZURECONTAINERREGISTRY": "testAcr",
"CONTAINERNAME": "testContainer",
"CONTAINERNAME": "testcontainer",
"CLUSTERRESOURCEGROUP": "testClusterRG",
"CLUSTERRESOURCETYPE": "Microsoft.ContainerService/managedClusters",
"CLUSTERNAME": "testCluster",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestGitHubWorkflowKustomizeTemplates(t *testing.T) {
"BRANCHNAME": "testBranch",
"ACRRESOURCEGROUP": "testAcrRG",
"AZURECONTAINERREGISTRY": "testAcr",
"CONTAINERNAME": "testContainer",
"CONTAINERNAME": "testcontainer",
"CLUSTERRESOURCEGROUP": "testClusterRG",
"CLUSTERRESOURCETYPE": "Microsoft.ContainerService/managedClusters",
"CLUSTERNAME": "testCluster",
Expand Down
Loading