diff --git a/.build/build.yaml b/.build/build.yaml new file mode 100644 index 00000000000..71578aa4249 --- /dev/null +++ b/.build/build.yaml @@ -0,0 +1,453 @@ +kind: Build +apiVersion: builds.katanomi.dev/v1alpha1 +spec: + workspaces: + - name: source + - name: source-tkn + - name: source-git-init + - name: cache + tasks: + ## read oss version + - name: oss-version + taskRef: + kind: ClusterTask + name: alauda-read-oss-version + params: + - name: version-file-path + value: ".build/opensource-version" + workspaces: + - name: source + workspace: source + - name: golang-test + taskRef: + kind: ClusterTask + name: go-unit-test + runAfter: + - oss-version + params: + - name: command + value: | + export GOPROXY=https://build-nexus.alauda.cn/repository/golang/,https://goproxy.cn,direct + export CGO_ENABLED=0 + export GONOSUMDB=* + export GOMAXPROCS=8 + go mod download + + go test -v -json -coverprofile cover.out ./... | tee test.json + + - name: quality-gate-rules + value: + - lines-coverage=10 + - passed-tests-rate=100 + - name: tool-image + # value: registry.alauda.cn:60080/devops/builder-go:1.18-ubuntu-495a3d88 + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + - name: test-report-path + value: ./test.json + - name: coverage-report-path + value: ./cover.out + workspaces: + - name: source + workspace: source + - name: cache + workspace: cache + ## ko publish image + - name: build-entrypoint + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/entrypoint" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/entrypoint" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + ## ko publish image + - name: build-pipeline-controller + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/controller" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/controller" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + ## ko publish image + - name: build-pipeline-webhook + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/webhook" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/webhook" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + ## ko publish image + - name: build-pipeline-nop + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/nop" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/nop" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + ## ko publish image + - name: build-pipeline-workingdirinit + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/workingdirinit" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/workingdirinit" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + ## ko publish image + - name: build-pipeline-resolvers + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/resolvers" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/resolvers" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + # ko publish image + - name: build-pipeline-events + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/events" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/events" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + # ko publish image + - name: build-pipeline-sidecarlogresults + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source + runAfter: + - oss-version + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/pipeline/cmd/sidecarlogresults" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/sidecarlogresults" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + + # clone tkn + - name: clone-tkn + timeout: 30m + retries: 0 + taskRef: + kind: ClusterTask + name: alauda-git-clone + workspaces: + - name: source + workspace: source-tkn + params: + - name: url + value: "https://github.com/tektoncd/cli.git" + - name: revision + # use the latest release tag + # https://github.com/tektoncd/cli/releases + value: "refs/tags/v0.34.0" + # modify the .ko.yaml + - name: modify-tkn-koyaml + timeout: 30m + retries: 0 + taskRef: + kind: ClusterTask + name: run-script + workspaces: + - name: source + workspace: source-tkn + runAfter: + - clone-git-init + params: + - name: command + value: | + cat < .ko.yaml + defaultBaseImage: build-harbor.alauda.cn/ops/distroless-static-nonroot:20220806 + EOF + ## ko publish image + - name: build-tkn + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + workspace: source-tkn + - name: cache + workspace: cache + runAfter: + - oss-version + - modify-tkn-koyaml + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd/cli/cmd/tkn" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/cli/cmd/tkn" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + + # clone git-init + - name: clone-git-init + timeout: 30m + retries: 0 + taskRef: + kind: ClusterTask + name: alauda-git-clone + workspaces: + - name: source + workspace: source-git-init + params: + - name: url + value: "https://github.com/tektoncd-catalog/git-clone.git" + - name: revision + # this component is not released yet, so use the latest commit + # value: "refs/heads/main" + value: "b33cc3e770a41900b59c197412c0c8ab9fa5a026" + # modify the .ko.yaml + - name: modify-git-init-koyaml + timeout: 30m + retries: 0 + taskRef: + kind: ClusterTask + name: run-script + workspaces: + - name: source + workspace: source-git-init + runAfter: + - clone-git-init + params: + - name: tool-image + value: build-harbor.alauda.cn/devops/builder-go:1.20-bullseye-48c4b76a + - name: command + value: | + # copy files to kodata, avoid next step build error + rm -rf image/git-init/kodata/LICENSE image/git-init/kodata/third_party + cp -rf LICENSE image/git-init/kodata/ + + export GOPROXY=https://build-nexus.alauda.cn/repository/golang/,https://goproxy.cn,direct + export CGO_ENABLED=0 + export GONOSUMDB=* + + # modify th go.mod to fix vuln + cd image/git-init/ + go get golang.org/x/net@v0.17.0 + go get google.golang.org/grpc@v1.56.3 + go mod tidy + go mod vendor + + cat < .ko.yaml + defaultBaseImage: build-harbor.alauda.cn/ops/distroless-static-nonroot:20220806 + baseImageOverrides: + # git-init uses a base image that includes Git, and supports running either + # as root or as user nonroot with UID 65532. + # image latest-glibc-hack is from katanomi tekton-operator hack Dockerfile + github.com/tektoncd-catalog/git-clone/image/git-init: build-harbor.alauda.cn/3rdparty/cgr.dev/chainguard/git:latest-glibc-hack-20231016 + EOF + ## ko publish image + - name: build-git-init + taskRef: + kind: ClusterTask + name: alauda-ko-build + workspaces: + - name: source + subPath: image/git-init + workspace: source-git-init + - name: cache + workspace: cache + runAfter: + - oss-version + - modify-git-init-koyaml + params: + - name: verbose + value: "false" + - name: import-path + value: "github.com/tektoncd-catalog/git-clone/git-init" + - name: container-image + value: "build-harbor.alauda.cn/devops/tektoncd/pipeline/cmd/git-init" + - name: container-image-tag + value: v$(tasks.oss-version.results.oss-version)-$(tasks.oss-version.results.commit-short-id) + + ################# + # code-scan + ################# + - name: code-scan + timeout: 30m + retries: 0 + runAfter: + - golang-test + taskRef: + kind: ClusterTask + name: sonarqube-analysis + workspaces: + - name: source + workspace: source + params: + - name: server + value: https://build-sonar.alauda.cn + + ################# + # runTemplate + ################# + runTemplate: + spec: + workspaces: + - name: cache + persistentVolumeClaim: + claimName: build-cache + - name: source-git-init + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1000Mi + - name: source-tkn + volumeClaimTemplate: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1000Mi + taskRunSpecs: + - pipelineTaskName: golang-test + stepOverrides: + - name: prepare + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + - name: test + resources: + requests: + cpu: "6" + memory: 4Gi + limits: + cpu: "8" + memory: 6Gi + - name: analysis + resources: + limits: + cpu: 100m + memory: 100Mi + requests: + cpu: 50m + memory: 50Mi + - pipelineTaskName: build-git-init + stepOverrides: + - name: build + resources: + requests: + cpu: "4" + memory: "4Gi" + limits: + cpu: "6" + memory: "6Gi" + - pipelineTaskName: modify-git-init-koyaml + stepOverrides: + - name: run + resources: + requests: + cpu: "500m" + memory: "512Mi" + limits: + cpu: "1" + memory: "1Gi" diff --git a/.build/opensource-version b/.build/opensource-version new file mode 100644 index 00000000000..316ba4bd9e6 --- /dev/null +++ b/.build/opensource-version @@ -0,0 +1 @@ +0.55.0 diff --git a/.ko.yaml b/.ko.yaml new file mode 100644 index 00000000000..608ffb44421 --- /dev/null +++ b/.ko.yaml @@ -0,0 +1,6 @@ +defaultBaseImage: build-harbor.alauda.cn/ops/distroless-static-nonroot:20220806 +baseImageOverrides: + # git-init uses a base image that includes Git, and supports running either + # as root or as user nonroot with UID 65532. + # image latest-glibc-hack is from katanomi tekton-operator hack Dockerfile + github.com/tektoncd/pipeline/cmd/git-init: build-harbor.alauda.cn/3rdparty/cgr.dev/chainguard/git:latest-glibc-hack-20231016 diff --git a/go.mod b/go.mod index 7511bd94f83..3b9a78d8456 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20220720053627-e327d0730470 // Waiting for https://github.com/ahmetb/gen-crd-api-reference-docs/pull/43/files to merge github.com/cloudevents/sdk-go/v2 v2.14.0 github.com/containerd/containerd v1.7.8 - github.com/go-git/go-git/v5 v5.10.0 + github.com/go-git/go-git/v5 v5.11.0 github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.16.1 github.com/google/uuid v1.4.0 @@ -65,7 +65,6 @@ require ( require ( github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/acomagu/bufpipe v1.0.4 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 @@ -93,7 +92,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.27.2 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect @@ -127,7 +126,7 @@ require ( github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect - github.com/skeema/knownhosts v1.2.0 // indirect + github.com/skeema/knownhosts v1.2.1 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/zeebo/errs v1.3.0 // indirect @@ -223,7 +222,7 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/automaxprocs v1.4.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/crypto v0.16.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.5.0 diff --git a/go.sum b/go.sum index 844de98d601..b7765dbd1ff 100644 --- a/go.sum +++ b/go.sum @@ -131,8 +131,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -247,8 +245,9 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudevents/sdk-go/v2 v2.14.0 h1:Nrob4FwVgi5L4tV9lhjzZcjYqFVyJzsA56CwPaPfv6s= github.com/cloudevents/sdk-go/v2 v2.14.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -468,8 +467,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmS github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= -github.com/go-git/go-git/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ= -github.com/go-git/go-git/v5 v5.10.0/go.mod h1:1FOZ/pQnqw24ghP2n7cunVl0ON55BsjPYvhWHvZGhoo= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -810,8 +809,6 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -1049,8 +1046,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= -github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1238,8 +1235,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= diff --git a/pkg/apis/pipeline/pod/template.go b/pkg/apis/pipeline/pod/template.go index e9f75fa3b74..b9272c9aac0 100644 --- a/pkg/apis/pipeline/pod/template.go +++ b/pkg/apis/pipeline/pod/template.go @@ -172,9 +172,8 @@ func MergePodTemplateWithDefault(tpl, defaultTpl *PodTemplate) *PodTemplate { return defaultTpl default: // Otherwise, merge fields - if tpl.Env == nil { - tpl.Env = defaultTpl.Env - } + tpl.Env = mergeByName(defaultTpl.Env, tpl.Env) + tpl.Volumes = mergeByName(defaultTpl.Volumes, tpl.Volumes) if tpl.NodeSelector == nil { tpl.NodeSelector = defaultTpl.NodeSelector } @@ -187,9 +186,6 @@ func MergePodTemplateWithDefault(tpl, defaultTpl *PodTemplate) *PodTemplate { if tpl.SecurityContext == nil { tpl.SecurityContext = defaultTpl.SecurityContext } - if tpl.Volumes == nil { - tpl.Volumes = defaultTpl.Volumes - } if tpl.RuntimeClassName == nil { tpl.RuntimeClassName = defaultTpl.RuntimeClassName } @@ -254,3 +250,48 @@ func MergeAAPodTemplateWithDefault(tpl, defaultTpl *AAPodTemplate) *AAPodTemplat return tpl } } + +// mergeByName merges two slices of items with names based on the getName +// function, giving priority to the items in the override slice. +func mergeByName[T any](base, overrides []T) []T { + if len(overrides) == 0 { + return base + } + + // create a map to store the names of the volumeVars in the override slice + seen := make(map[string]struct{}) + result := make([]T, 0, len(base)+len(overrides)) + + for _, item := range overrides { + name := getName(item) + if name != "" { + result = append(result, item) + seen[name] = struct{}{} + } + } + + // append the volumeVars in the original slice if they have a different name + for _, item := range base { + name := getName(item) + if name != "" { + if _, found := seen[name]; !found { + result = append(result, item) + } + } + } + + return result +} + +// getName returns the name of the given item, or an empty string if the item +// is not a supported type. +func getName(item interface{}) string { + switch item := item.(type) { + case corev1.EnvVar: + return item.Name + case corev1.Volume: + return item.Name + default: + return "" + } +} diff --git a/pkg/apis/pipeline/v1/container_types.go b/pkg/apis/pipeline/v1/container_types.go index b7da45e0b57..d99c8e8508c 100644 --- a/pkg/apis/pipeline/v1/container_types.go +++ b/pkg/apis/pipeline/v1/container_types.go @@ -140,7 +140,6 @@ type Step struct { Ref *Ref `json:"ref,omitempty"` // Params declares parameters passed to this step action. // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // Results declares StepResults produced by the Step. // diff --git a/pkg/apis/pipeline/v1/matrix_types.go b/pkg/apis/pipeline/v1/matrix_types.go index f51a0ac4af4..3f7cb4eaf9f 100644 --- a/pkg/apis/pipeline/v1/matrix_types.go +++ b/pkg/apis/pipeline/v1/matrix_types.go @@ -31,16 +31,15 @@ type Matrix struct { // Params takes only `Parameters` of type `"array"` // Each array element is supplied to the `PipelineTask` by substituting `params` of type `"string"` in the underlying `Task`. // The names of the `params` in the `Matrix` must match the names of the `params` in the underlying `Task` that they will be substituting. - // +listType=atomic Params Params `json:"params,omitempty"` // Include is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix. // +optional - // +listType=atomic Include IncludeParamsList `json:"include,omitempty"` } // IncludeParamsList is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix. +// +listType=atomic type IncludeParamsList []IncludeParams // IncludeParams allows passing in a specific combinations of Parameters into the Matrix. @@ -50,7 +49,6 @@ type IncludeParams struct { // Params takes only `Parameters` of type `"string"` // The names of the `params` must match the names of the `params` in the underlying `Task` - // +listType=atomic Params Params `json:"params,omitempty"` } diff --git a/pkg/apis/pipeline/v1/param_types.go b/pkg/apis/pipeline/v1/param_types.go index c5973544221..e9f06883b0e 100644 --- a/pkg/apis/pipeline/v1/param_types.go +++ b/pkg/apis/pipeline/v1/param_types.go @@ -62,6 +62,7 @@ type ParamSpec struct { } // ParamSpecs is a list of ParamSpec +// +listType=atomic type ParamSpecs []ParamSpec // PropertySpec defines the struct for object keys @@ -268,6 +269,7 @@ func (p Param) ParseTaskandResultName() (string, string) { } // Params is a list of Param +// +listType=atomic type Params []Param // ExtractParamArrayLengths extract and return the lengths of all array params @@ -482,11 +484,11 @@ var AllParamTypes = []ParamType{ParamTypeString, ParamTypeArray, ParamTypeObject // Used in JSON unmarshalling so that a single JSON field can accept // either an individual string or an array of strings. type ParamValue struct { - Type ParamType // Represents the stored type of ParamValues. - StringVal string + Type ParamType `json:"type"` // Represents the stored type of ParamValues. + StringVal string `json:"stringVal"` // +listType=atomic - ArrayVal []string - ObjectVal map[string]string + ArrayVal []string `json:"arrayVal"` + ObjectVal map[string]string `json:"objectVal"` } // UnmarshalJSON implements the json.Unmarshaller interface. diff --git a/pkg/apis/pipeline/v1/pipeline_types.go b/pkg/apis/pipeline/v1/pipeline_types.go index 3cbc5295149..1a8ca46f4ec 100644 --- a/pkg/apis/pipeline/v1/pipeline_types.go +++ b/pkg/apis/pipeline/v1/pipeline_types.go @@ -114,7 +114,6 @@ type PipelineSpec struct { Tasks []PipelineTask `json:"tasks,omitempty"` // Params declares a list of input parameters that must be supplied when // this Pipeline is run. - // +listType=atomic Params ParamSpecs `json:"params,omitempty"` // Workspaces declares a set of named workspaces that are expected to be // provided by a PipelineRun. @@ -218,7 +217,6 @@ type PipelineTask struct { // Parameters declares parameters passed to this task. // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // Matrix declares parameters used to fan out this task. diff --git a/pkg/apis/pipeline/v1/pipelinerun_types.go b/pkg/apis/pipeline/v1/pipelinerun_types.go index 34be57243ae..cdcd32ee744 100644 --- a/pkg/apis/pipeline/v1/pipelinerun_types.go +++ b/pkg/apis/pipeline/v1/pipelinerun_types.go @@ -251,7 +251,6 @@ type PipelineRunSpec struct { // +optional PipelineSpec *PipelineSpec `json:"pipelineSpec,omitempty"` // Params is a list of parameter names and values. - // +listType=atomic Params Params `json:"params,omitempty"` // Used for cancelling a pipelinerun (and maybe more later on) diff --git a/pkg/apis/pipeline/v1/resolver_types.go b/pkg/apis/pipeline/v1/resolver_types.go index 095a9d00a28..66032992c25 100644 --- a/pkg/apis/pipeline/v1/resolver_types.go +++ b/pkg/apis/pipeline/v1/resolver_types.go @@ -33,6 +33,5 @@ type ResolverRef struct { // "repo" or "path" but the set of params ultimately depends on // the chosen resolver. // +optional - // +listType=atomic Params Params `json:"params,omitempty"` } diff --git a/pkg/apis/pipeline/v1/task_types.go b/pkg/apis/pipeline/v1/task_types.go index 894590508ff..d3899edcc55 100644 --- a/pkg/apis/pipeline/v1/task_types.go +++ b/pkg/apis/pipeline/v1/task_types.go @@ -80,7 +80,6 @@ type TaskSpec struct { // must be supplied as inputs in TaskRuns unless they declare a default // value. // +optional - // +listType=atomic Params ParamSpecs `json:"params,omitempty"` // DisplayName is a user-facing name of the task that may be diff --git a/pkg/apis/pipeline/v1/taskrun_types.go b/pkg/apis/pipeline/v1/taskrun_types.go index 7c3cf232ee5..b8c42618551 100644 --- a/pkg/apis/pipeline/v1/taskrun_types.go +++ b/pkg/apis/pipeline/v1/taskrun_types.go @@ -36,7 +36,6 @@ type TaskRunSpec struct { // +optional Debug *TaskRunDebug `json:"debug,omitempty"` // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // +optional ServiceAccountName string `json:"serviceAccountName"` diff --git a/pkg/apis/pipeline/v1alpha1/stepaction_types.go b/pkg/apis/pipeline/v1alpha1/stepaction_types.go index faee28dc2e5..e33679efa1a 100644 --- a/pkg/apis/pipeline/v1alpha1/stepaction_types.go +++ b/pkg/apis/pipeline/v1alpha1/stepaction_types.go @@ -121,7 +121,6 @@ type StepActionSpec struct { // Params is a list of input parameters required to run the stepAction. // Params must be supplied as inputs in Steps unless they declare a defaultvalue. // +optional - // +listType=atomic Params v1.ParamSpecs `json:"params,omitempty"` // Results are values that this StepAction can output // +optional diff --git a/pkg/apis/pipeline/v1beta1/container_types.go b/pkg/apis/pipeline/v1beta1/container_types.go index 2b2cf7901aa..132870d4ade 100644 --- a/pkg/apis/pipeline/v1beta1/container_types.go +++ b/pkg/apis/pipeline/v1beta1/container_types.go @@ -235,7 +235,6 @@ type Step struct { Ref *Ref `json:"ref,omitempty"` // Params declares parameters passed to this step action. // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // Results declares StepResults produced by the Step. // diff --git a/pkg/apis/pipeline/v1beta1/customrun_types.go b/pkg/apis/pipeline/v1beta1/customrun_types.go index e74f48342b1..c1459094e17 100644 --- a/pkg/apis/pipeline/v1beta1/customrun_types.go +++ b/pkg/apis/pipeline/v1beta1/customrun_types.go @@ -53,7 +53,6 @@ type CustomRunSpec struct { CustomSpec *EmbeddedCustomRunSpec `json:"customSpec,omitempty"` // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // Used for cancelling a customrun (and maybe more later on) diff --git a/pkg/apis/pipeline/v1beta1/matrix_types.go b/pkg/apis/pipeline/v1beta1/matrix_types.go index 19042e11e4b..e85182ab830 100644 --- a/pkg/apis/pipeline/v1beta1/matrix_types.go +++ b/pkg/apis/pipeline/v1beta1/matrix_types.go @@ -31,16 +31,15 @@ type Matrix struct { // Params takes only `Parameters` of type `"array"` // Each array element is supplied to the `PipelineTask` by substituting `params` of type `"string"` in the underlying `Task`. // The names of the `params` in the `Matrix` must match the names of the `params` in the underlying `Task` that they will be substituting. - // +listType=atomic Params Params `json:"params,omitempty"` // Include is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix. // +optional - // +listType=atomic Include IncludeParamsList `json:"include,omitempty"` } // IncludeParamsList is a list of IncludeParams which allows passing in specific combinations of Parameters into the Matrix. +// +listType=atomic type IncludeParamsList []IncludeParams // IncludeParams allows passing in a specific combinations of Parameters into the Matrix. @@ -50,7 +49,6 @@ type IncludeParams struct { // Params takes only `Parameters` of type `"string"` // The names of the `params` must match the names of the `params` in the underlying `Task` - // +listType=atomic Params Params `json:"params,omitempty"` } diff --git a/pkg/apis/pipeline/v1beta1/param_types.go b/pkg/apis/pipeline/v1beta1/param_types.go index f0634f8c580..648cf4463b4 100644 --- a/pkg/apis/pipeline/v1beta1/param_types.go +++ b/pkg/apis/pipeline/v1beta1/param_types.go @@ -62,6 +62,7 @@ type ParamSpec struct { } // ParamSpecs is a list of ParamSpec +// +listType=atomic type ParamSpecs []ParamSpec // PropertySpec defines the struct for object keys @@ -191,6 +192,7 @@ type Param struct { } // Params is a list of Param +// +listType=atomic type Params []Param // ExtractNames returns a set of unique names @@ -436,11 +438,11 @@ var AllParamTypes = []ParamType{ParamTypeString, ParamTypeArray, ParamTypeObject // Used in JSON unmarshalling so that a single JSON field can accept // either an individual string or an array of strings. type ParamValue struct { - Type ParamType // Represents the stored type of ParamValues. - StringVal string + Type ParamType `json:"type"` // Represents the stored type of ParamValues. + StringVal string `json:"stringVal"` // +listType=atomic - ArrayVal []string - ObjectVal map[string]string + ArrayVal []string `json:"arrayVal"` + ObjectVal map[string]string `json:"objectVal"` } // ArrayOrString is deprecated, this is to keep backward compatibility diff --git a/pkg/apis/pipeline/v1beta1/pipeline_types.go b/pkg/apis/pipeline/v1beta1/pipeline_types.go index 256f24f1dcf..9d4e014f503 100644 --- a/pkg/apis/pipeline/v1beta1/pipeline_types.go +++ b/pkg/apis/pipeline/v1beta1/pipeline_types.go @@ -124,7 +124,6 @@ type PipelineSpec struct { Tasks []PipelineTask `json:"tasks,omitempty"` // Params declares a list of input parameters that must be supplied when // this Pipeline is run. - // +listType=atomic Params ParamSpecs `json:"params,omitempty"` // Workspaces declares a set of named workspaces that are expected to be // provided by a PipelineRun. @@ -232,7 +231,6 @@ type PipelineTask struct { // Parameters declares parameters passed to this task. // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // Matrix declares parameters used to fan out this task. diff --git a/pkg/apis/pipeline/v1beta1/pipelinerun_types.go b/pkg/apis/pipeline/v1beta1/pipelinerun_types.go index 4c7ae064567..6a98cc90b53 100644 --- a/pkg/apis/pipeline/v1beta1/pipelinerun_types.go +++ b/pkg/apis/pipeline/v1beta1/pipelinerun_types.go @@ -262,7 +262,6 @@ type PipelineRunSpec struct { // +listType=atomic Resources []PipelineResourceBinding `json:"resources,omitempty"` // Params is a list of parameter names and values. - // +listType=atomic Params Params `json:"params,omitempty"` // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` diff --git a/pkg/apis/pipeline/v1beta1/resolver_types.go b/pkg/apis/pipeline/v1beta1/resolver_types.go index 70b1c788672..34e49278983 100644 --- a/pkg/apis/pipeline/v1beta1/resolver_types.go +++ b/pkg/apis/pipeline/v1beta1/resolver_types.go @@ -32,6 +32,5 @@ type ResolverRef struct { // "repo" or "path" but the set of params ultimately depends on // the chosen resolver. // +optional - // +listType=atomic Params Params `json:"params,omitempty"` } diff --git a/pkg/apis/pipeline/v1beta1/task_types.go b/pkg/apis/pipeline/v1beta1/task_types.go index 2961ade3be6..eff01670c0d 100644 --- a/pkg/apis/pipeline/v1beta1/task_types.go +++ b/pkg/apis/pipeline/v1beta1/task_types.go @@ -104,7 +104,6 @@ type TaskSpec struct { // must be supplied as inputs in TaskRuns unless they declare a default // value. // +optional - // +listType=atomic Params ParamSpecs `json:"params,omitempty"` // DisplayName is a user-facing name of the task that may be diff --git a/pkg/apis/pipeline/v1beta1/taskrun_types.go b/pkg/apis/pipeline/v1beta1/taskrun_types.go index a12676acb0a..a21877d7e43 100644 --- a/pkg/apis/pipeline/v1beta1/taskrun_types.go +++ b/pkg/apis/pipeline/v1beta1/taskrun_types.go @@ -39,7 +39,6 @@ type TaskRunSpec struct { // +optional Debug *TaskRunDebug `json:"debug,omitempty"` // +optional - // +listType=atomic Params Params `json:"params,omitempty"` // Deprecated: Unused, preserved only for backwards compatibility // +optional diff --git a/pkg/credentials/dockercreds/creds.go b/pkg/credentials/dockercreds/creds.go index d6a4dd4c31b..dca0bd80528 100644 --- a/pkg/credentials/dockercreds/creds.go +++ b/pkg/credentials/dockercreds/creds.go @@ -76,10 +76,6 @@ func (dc *basicDocker) Set(value string) error { secret := parts[0] url := parts[1] - if _, ok := dc.Entries[url]; ok { - return fmt.Errorf("multiple entries for url: %v", url) - } - e, err := newEntry(secret) if err != nil { return err diff --git a/pkg/credentials/gitcreds/basic.go b/pkg/credentials/gitcreds/basic.go index aee42c70089..90021e7aabe 100644 --- a/pkg/credentials/gitcreds/basic.go +++ b/pkg/credentials/gitcreds/basic.go @@ -57,10 +57,6 @@ func (dc *basicGitConfig) Set(value string) error { secret := parts[0] url := parts[1] - if _, ok := dc.entries[url]; ok { - return fmt.Errorf("multiple entries for url: %v", url) - } - e, err := newBasicEntry(url, secret) if err != nil { return err diff --git a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go index b765ff591ae..92961f3528e 100644 --- a/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go +++ b/pkg/reconciler/pipelinerun/resources/pipelinerunresolution_test.go @@ -5259,17 +5259,17 @@ func TestValidateParamEnumSubset_Invalid(t *testing.T) { }, }, wantErr: fmt.Errorf("pipeline param \"p1\" has no enum, but referenced in \"ref1\" task has enums: [v1 v3]"), - }, { - name: "invalid param syntax - failure", - params: []v1.Param{ - { - Name: "resolved-task-p1", - Value: v1.ParamValue{ - StringVal: "$(params.p1.aaa.bbb)", - }, - }, - }, - wantErr: fmt.Errorf("unexpected error in ExtractVariablesFromString: Invalid referencing of parameters in \"$(params.p1.aaa.bbb)\"! Only two dot-separated components after the prefix \"params\" are allowed."), + // }, { + // name: "invalid param syntax - failure", + // params: []v1.Param{ + // { + // Name: "resolved-task-p1", + // Value: v1.ParamValue{ + // StringVal: "$(params.p1.aaa.bbb)", + // }, + // }, + // }, + // wantErr: fmt.Errorf("unexpected error in ExtractVariablesFromString: Invalid referencing of parameters in \"$(params.p1.aaa.bbb)\"! Only two dot-separated components after the prefix \"params\" are allowed."), }} for _, tc := range tcs { diff --git a/pkg/remote/resolution/resolver.go b/pkg/remote/resolution/resolver.go index 772b39e416a..0e8a4b72fc4 100644 --- a/pkg/remote/resolution/resolver.go +++ b/pkg/remote/resolution/resolver.go @@ -23,7 +23,9 @@ import ( "github.com/tektoncd/pipeline/pkg/remote" resolutioncommon "github.com/tektoncd/pipeline/pkg/resolution/common" remoteresource "github.com/tektoncd/pipeline/pkg/resolution/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "knative.dev/pkg/kmap" "knative.dev/pkg/kmeta" ) @@ -80,6 +82,15 @@ func (resolver *Resolver) Get(ctx context.Context, _, _ string) (runtime.Object, if err != nil { return nil, nil, &InvalidRuntimeObjectError{original: err} } + + // Copy annotations from the resolved resource to the runtime object. + if new, ok := obj.(metav1.Object); ok { + new.SetAnnotations(kmap.Union(resolved.Annotations(), new.GetAnnotations())) + if len(new.GetAnnotations()) == 0 { + new.SetAnnotations(nil) + } + obj = new.(runtime.Object) + } return obj, resolved.RefSource(), nil } diff --git a/pkg/remote/resolution/resolver_test.go b/pkg/remote/resolution/resolver_test.go index 8e900ba50ea..1125da50c3b 100644 --- a/pkg/remote/resolution/resolver_test.go +++ b/pkg/remote/resolution/resolver_test.go @@ -34,6 +34,8 @@ kind: Pipeline apiVersion: tekton.dev/v1beta1 metadata: name: foo + annotations: + foo: bar spec: tasks: - name: task1 @@ -69,8 +71,13 @@ func TestGet_Successful(t *testing.T) { ResolvedResource: resolved, } resolver := NewResolver(requester, owner, "git", "", "", nil) - if _, _, err := resolver.Get(ctx, "foo", "bar"); err != nil { + if obj, _, err := resolver.Get(ctx, "foo", "bar"); err != nil { t.Fatalf("unexpected error: %v", err) + } else { + annotations := obj.(metav1.Object).GetAnnotations() + if annotations["foo"] != "bar" { + t.Fatalf("expected annotations to be set") + } } } } diff --git a/pkg/substitution/substitution.go b/pkg/substitution/substitution.go index 8e1acab2fe1..7531ad5acef 100644 --- a/pkg/substitution/substitution.go +++ b/pkg/substitution/substitution.go @@ -48,6 +48,111 @@ var paramIndexingRegex = regexp.MustCompile(paramIndexing) // intIndexRegex will match all `[int]` for param expression var intIndexRegex = regexp.MustCompile(intIndex) +// ValidateVariable makes sure all variables in the provided string are known +func ValidateVariable(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { + if vs, present, _ := ExtractVariablesFromString(value, prefix); present { + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if !vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("non-existent variable in %q for %s %s", value, locationName, name), + Paths: []string{path + "." + name}, + } + } + } + } + return nil +} + +// ValidateVariableP makes sure all variables for a parameter in the provided string are known +func ValidateVariableP(value, prefix string, vars sets.String) *apis.FieldError { + if vs, present, errString := ExtractVariablesFromString(value, prefix); present { + if errString != "" { + return &apis.FieldError{ + Message: errString, + Paths: []string{""}, + } + + } + for _, v := range vs { + v = TrimArrayIndex(v) + if !vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("non-existent variable in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + } + return nil +} + +// ValidateVariableProhibited verifies that variables matching the relevant string expressions do not reference any of the names present in vars. +func ValidateVariableProhibited(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { + if vs, present, _ := ExtractVariablesFromString(value, prefix); present { + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable type invalid in %q for %s %s", value, locationName, name), + Paths: []string{path + "." + name}, + } + } + } + } + return nil +} + +// ValidateVariableProhibitedP verifies that variables for a parameter matching the relevant string expressions do not reference any of the names present in vars. +func ValidateVariableProhibitedP(value, prefix string, vars sets.String) *apis.FieldError { + if vs, present, errString := ExtractVariablesFromString(value, prefix); present { + if errString != "" { + return &apis.FieldError{ + Message: errString, + Paths: []string{""}, + } + + } + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable type invalid in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + } + return nil +} + +// ValidateEntireVariableProhibitedP verifies that values of object type are not used as whole. +func ValidateEntireVariableProhibitedP(value, prefix string, vars sets.String) *apis.FieldError { + vs, err := extractEntireVariablesFromString(value, prefix) + if err != nil { + return &apis.FieldError{ + Message: fmt.Sprintf("extractEntireVariablesFromString failed : %v", err), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable type invalid in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + + return nil +} + // ValidateNoReferencesToUnknownVariables returns an error if the input string contains references to unknown variables // Inputs: // - value: a string containing a reference to a variable that can be substituted, e.g. "echo $(params.foo)" @@ -171,6 +276,52 @@ func ValidateVariableReferenceIsIsolated(value, prefix string, vars sets.String) return nil } +// ValidateVariableIsolated verifies that variables matching the relevant string expressions are completely isolated if present. +func ValidateVariableIsolated(name, value, prefix, locationName, path string, vars sets.String) *apis.FieldError { + if vs, present, _ := ExtractVariablesFromString(value, prefix); present { + firstMatch, _ := extractExpressionFromString(value, prefix) + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + if len(value) != len(firstMatch) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable is not properly isolated in %q for %s %s", value, locationName, name), + Paths: []string{path + "." + name}, + } + } + } + } + } + return nil +} + +// ValidateVariableIsolatedP verifies that variables matching the relevant string expressions are completely isolated if present. +func ValidateVariableIsolatedP(value, prefix string, vars sets.String) *apis.FieldError { + if vs, present, errString := ExtractVariablesFromString(value, prefix); present { + if errString != "" { + return &apis.FieldError{ + Message: errString, + Paths: []string{""}, + } + + } + firstMatch, _ := extractExpressionFromString(value, prefix) + for _, v := range vs { + v = strings.TrimSuffix(v, "[*]") + if vars.Has(v) { + if len(value) != len(firstMatch) { + return &apis.FieldError{ + Message: fmt.Sprintf("variable is not properly isolated in %q", value), + // Empty path is required to make the `ViaField`, … work + Paths: []string{""}, + } + } + } + } + } + return nil +} + // ValidateWholeArrayOrObjectRefInStringVariable validates if a single string field uses references to the whole array/object appropriately // valid example: "$(params.myObject[*])" // invalid example: "$(params.name-not-exist[*])" @@ -237,10 +388,10 @@ func ExtractVariablesFromString(s, prefix string) ([]string, bool, string) { // Invalid Examples: // - .foo.bar.baz.... if j == 0 && strings.Contains(val, ".") { - if len(strings.Split(val, ".")) > 2 { - errString = fmt.Sprintf(`Invalid referencing of parameters in "%s"! Only two dot-separated components after the prefix "%s" are allowed.`, s, prefix) - return vars, true, errString - } + // if len(strings.Split(val, ".")) > 2 { + // errString = fmt.Sprintf(`Invalid referencing of parameters in "%s"! Only two dot-separated components after the prefix "%s" are allowed.`, s, prefix) + // return vars, true, errString + // } vars[i] = strings.SplitN(val, ".", 2)[0] break } diff --git a/pkg/substitution/substitution_test.go b/pkg/substitution/substitution_test.go index c265510d195..f4db2ca5faf 100644 --- a/pkg/substitution/substitution_test.go +++ b/pkg/substitution/substitution_test.go @@ -18,7 +18,6 @@ limitations under the License. package substitution_test import ( - "fmt" "testing" "github.com/google/go-cmp/cmp" @@ -78,17 +77,17 @@ func TestValidateNoReferencesToUnknownVariables(t *testing.T) { vars: sets.NewString("foo.bar.baz"), }, expectedError: nil, - }, { - name: "invalid variable with only dots referencing parameters", - args: args{ - input: "--flag=$(params.foo.bar.baz)", - prefix: "params", - vars: sets.NewString("foo.bar.baz"), - }, - expectedError: &apis.FieldError{ - Message: fmt.Sprintf(`Invalid referencing of parameters in "%s"! Only two dot-separated components after the prefix "%s" are allowed.`, "--flag=$(params.foo.bar.baz)", "params"), - Paths: []string{""}, - }, + // }, { + // name: "invalid variable with only dots referencing parameters", + // args: args{ + // input: "--flag=$(params.foo.bar.baz)", + // prefix: "params", + // vars: sets.NewString("foo.bar.baz"), + // }, + // expectedError: &apis.FieldError{ + // Message: fmt.Sprintf(`Invalid referencing of parameters in "%s"! Only two dot-separated components after the prefix "%s" are allowed.`, "--flag=$(params.foo.bar.baz)", "params"), + // Paths: []string{""}, + // }, }, { name: "valid variable with dots referencing resources", args: args{ @@ -97,17 +96,17 @@ func TestValidateNoReferencesToUnknownVariables(t *testing.T) { vars: sets.NewString("foo"), }, expectedError: nil, - }, { - name: "invalid variable with dots referencing resources", - args: args{ - input: "--flag=$(resources.inputs.foo.bar.baz)", - prefix: "resources.(?:inputs|outputs)", - vars: sets.NewString("foo.bar"), - }, - expectedError: &apis.FieldError{ - Message: fmt.Sprintf(`Invalid referencing of parameters in "%s"! Only two dot-separated components after the prefix "%s" are allowed.`, "--flag=$(resources.inputs.foo.bar.baz)", "resources.(?:inputs|outputs)"), - Paths: []string{""}, - }, + // }, { + // name: "invalid variable with dots referencing resources", + // args: args{ + // input: "--flag=$(resources.inputs.foo.bar.baz)", + // prefix: "resources.(?:inputs|outputs)", + // vars: sets.NewString("foo.bar"), + // }, + // expectedError: &apis.FieldError{ + // Message: fmt.Sprintf(`Invalid referencing of parameters in "%s"! Only two dot-separated components after the prefix "%s" are allowed.`, "--flag=$(resources.inputs.foo.bar.baz)", "resources.(?:inputs|outputs)"), + // Paths: []string{""}, + // }, }, { name: "valid variable contains diffetent chars", args: args{ @@ -730,13 +729,13 @@ func TestExtractVariablesFromString(t *testing.T) { want: []string{}, extracted: false, err: "", - }, { - name: "too many dots", - s: "--flag=$(inputs.params.foo.baz.bar)", - prefix: "inputs.params", - want: []string{""}, - extracted: true, - err: `Invalid referencing of parameters in "--flag=$(inputs.params.foo.baz.bar)"! Only two dot-separated components after the prefix "inputs.params" are allowed.`, + // }, { + // name: "too many dots", + // s: "--flag=$(inputs.params.foo.baz.bar)", + // prefix: "inputs.params", + // want: []string{""}, + // extracted: true, + // err: `Invalid referencing of parameters in "--flag=$(inputs.params.foo.baz.bar)"! Only two dot-separated components after the prefix "inputs.params" are allowed.`, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000000..5ed63fb3949 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,19 @@ +# if you want disabled the DTD verification for a proxy problem for example, true by default +sonar.projectKey=github.com-tektoncd-pipeline.git +sonar.projectName=tekton-pipeline + +sonar.coverage.dtdVerification=false +sonar.sourceEncoding=UTF-8 + +sonar.sources=. +sonar.exclusions=vendor/**,test/**,**/*_generated*go,**/generated.pb.go,**/*.md,bin/*,testbin/*,**/*.yaml,**/Dockerfile,go.mod,go.sum + +sonar.tests=. +sonar.test.inclusions=**/*_test.go +sonar.test.exclusions=vendor/**,test/**,**/*_generated*go,**/generated.pb.go,**/*.md,bin/*,testbin/*,**/*.yaml,go.mod,go.sum + +# https://docs.sonarqube.org/latest/project-administration/narrowing-the-focus/#header-5 +sonar.coverage.exclusions=vendor/**,test/**,**/*_generated*go,**/generated.pb.go,**/*.md,bin/*,testbin/*,**/*.yaml,go.mod,go.sum + +sonar.links.scm=https://github.com/katanomi/tekton-pipeline + diff --git a/vendor/github.com/acomagu/bufpipe/CREDITS b/vendor/github.com/acomagu/bufpipe/CREDITS deleted file mode 100644 index fb0d2b59a02..00000000000 --- a/vendor/github.com/acomagu/bufpipe/CREDITS +++ /dev/null @@ -1,60 +0,0 @@ -Go (the standard library) -https://golang.org/ ----------------------------------------------------------------- -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -================================================================ - -github.com/matryer/is -https://github.com/matryer/is ----------------------------------------------------------------- -MIT License - -Copyright (c) 2017-2018 Mat Ryer - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -================================================================ - diff --git a/vendor/github.com/acomagu/bufpipe/LICENSE b/vendor/github.com/acomagu/bufpipe/LICENSE deleted file mode 100644 index 1b5f21cd242..00000000000 --- a/vendor/github.com/acomagu/bufpipe/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 acomagu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/acomagu/bufpipe/README.md b/vendor/github.com/acomagu/bufpipe/README.md deleted file mode 100644 index 136ee01db16..00000000000 --- a/vendor/github.com/acomagu/bufpipe/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# bufpipe: Buffered Pipe - -[![CircleCI](https://img.shields.io/circleci/build/github/acomagu/bufpipe.svg?style=flat-square)](https://circleci.com/gh/acomagu/bufpipe) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/acomagu/bufpipe) - -The buffered version of io.Pipe. It's safe for concurrent use. - -## How does it differ from io.Pipe? - -Writes never block because the pipe has variable-sized buffer. - -```Go -r, w := bufpipe.New(nil) -io.WriteString(w, "abc") // No blocking. -io.WriteString(w, "def") // No blocking, too. -w.Close() -io.Copy(os.Stdout, r) -// Output: abcdef -``` - -[Playground](https://play.golang.org/p/PdyBAS3pVob) - -## How does it differ from bytes.Buffer? - -Reads block if the internal buffer is empty until the writer is closed. - -```Go -r, w := bufpipe.New(nil) - -done := make(chan struct{}) -go func() { - io.Copy(os.Stdout, r) // The reads block until the writer is closed. - done <- struct{}{} -}() - -io.WriteString(w, "abc") -io.WriteString(w, "def") -w.Close() -<-done -// Output: abcdef -``` - -[Playground](https://play.golang.org/p/UppmyLeRgX6) - -## Contribution - -### Generate CREDITS - -The [CREDITS](./CREDITS) file are generated by [gocredits](https://github.com/Songmu/gocredits). Update it when the dependencies are changed. - -``` -$ gocredits > CREDITS -``` diff --git a/vendor/github.com/acomagu/bufpipe/bufpipe.go b/vendor/github.com/acomagu/bufpipe/bufpipe.go deleted file mode 100644 index 34882ea0806..00000000000 --- a/vendor/github.com/acomagu/bufpipe/bufpipe.go +++ /dev/null @@ -1,129 +0,0 @@ -package bufpipe - -import ( - "bytes" - "errors" - "io" - "sync" -) - -// ErrClosedPipe is the error used for read or write operations on a closed pipe. -var ErrClosedPipe = errors.New("bufpipe: read/write on closed pipe") - -type pipe struct { - cond *sync.Cond - buf *bytes.Buffer - rerr, werr error -} - -// A PipeReader is the read half of a pipe. -type PipeReader struct { - *pipe -} - -// A PipeWriter is the write half of a pipe. -type PipeWriter struct { - *pipe -} - -// New creates a synchronous pipe using buf as its initial contents. It can be -// used to connect code expecting an io.Reader with code expecting an io.Writer. -// -// Unlike io.Pipe, writes never block because the internal buffer has variable -// size. Reads block only when the buffer is empty. -// -// It is safe to call Read and Write in parallel with each other or with Close. -// Parallel calls to Read and parallel calls to Write are also safe: the -// individual calls will be gated sequentially. -// -// The new pipe takes ownership of buf, and the caller should not use buf after -// this call. New is intended to prepare a PipeReader to read existing data. It -// can also be used to set the initial size of the internal buffer for writing. -// To do that, buf should have the desired capacity but a length of zero. -func New(buf []byte) (*PipeReader, *PipeWriter) { - p := &pipe{ - buf: bytes.NewBuffer(buf), - cond: sync.NewCond(new(sync.Mutex)), - } - return &PipeReader{ - pipe: p, - }, &PipeWriter{ - pipe: p, - } -} - -// Read implements the standard Read interface: it reads data from the pipe, -// reading from the internal buffer, otherwise blocking until a writer arrives -// or the write end is closed. If the write end is closed with an error, that -// error is returned as err; otherwise err is io.EOF. -func (r *PipeReader) Read(data []byte) (int, error) { - r.cond.L.Lock() - defer r.cond.L.Unlock() - -RETRY: - n, err := r.buf.Read(data) - // If not closed and no read, wait for writing. - if err == io.EOF && r.rerr == nil && n == 0 { - r.cond.Wait() - goto RETRY - } - if err == io.EOF { - return n, r.rerr - } - return n, err -} - -// Close closes the reader; subsequent writes from the write half of the pipe -// will return error ErrClosedPipe. -func (r *PipeReader) Close() error { - return r.CloseWithError(nil) -} - -// CloseWithError closes the reader; subsequent writes to the write half of the -// pipe will return the error err. -func (r *PipeReader) CloseWithError(err error) error { - r.cond.L.Lock() - defer r.cond.L.Unlock() - - if err == nil { - err = ErrClosedPipe - } - r.werr = err - return nil -} - -// Write implements the standard Write interface: it writes data to the internal -// buffer. If the read end is closed with an error, that err is returned as err; -// otherwise err is ErrClosedPipe. -func (w *PipeWriter) Write(data []byte) (int, error) { - w.cond.L.Lock() - defer w.cond.L.Unlock() - - if w.werr != nil { - return 0, w.werr - } - - n, err := w.buf.Write(data) - w.cond.Signal() - return n, err -} - -// Close closes the writer; subsequent reads from the read half of the pipe will -// return io.EOF once the internal buffer get empty. -func (w *PipeWriter) Close() error { - return w.CloseWithError(nil) -} - -// Close closes the writer; subsequent reads from the read half of the pipe will -// return err once the internal buffer get empty. -func (w *PipeWriter) CloseWithError(err error) error { - w.cond.L.Lock() - defer w.cond.L.Unlock() - - if err == nil { - err = io.EOF - } - w.rerr = err - w.cond.Broadcast() - return nil -} diff --git a/vendor/github.com/acomagu/bufpipe/doc.go b/vendor/github.com/acomagu/bufpipe/doc.go deleted file mode 100644 index 16a39480017..00000000000 --- a/vendor/github.com/acomagu/bufpipe/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package bufpipe provides a IO pipe, has variable-sized buffer. -package bufpipe diff --git a/vendor/github.com/cloudflare/circl/ecc/goldilocks/twist.go b/vendor/github.com/cloudflare/circl/ecc/goldilocks/twist.go index 8cd4e333b96..83d7cdadd3e 100644 --- a/vendor/github.com/cloudflare/circl/ecc/goldilocks/twist.go +++ b/vendor/github.com/cloudflare/circl/ecc/goldilocks/twist.go @@ -9,7 +9,7 @@ import ( fp "github.com/cloudflare/circl/math/fp448" ) -// twistCurve is -x^2+y^2=1-39082x^2y^2 and is 4-isogeneous to Goldilocks. +// twistCurve is -x^2+y^2=1-39082x^2y^2 and is 4-isogenous to Goldilocks. type twistCurve struct{} // Identity returns the identity point. diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go index ab19d0ad124..1755fd1e6dc 100644 --- a/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go +++ b/vendor/github.com/cloudflare/circl/internal/sha3/keccakf.go @@ -6,13 +6,21 @@ package sha3 // KeccakF1600 applies the Keccak permutation to a 1600b-wide // state represented as a slice of 25 uint64s. +// If turbo is true, applies the 12-round variant instead of the +// regular 24-round variant. // nolint:funlen -func KeccakF1600(a *[25]uint64) { +func KeccakF1600(a *[25]uint64, turbo bool) { // Implementation translated from Keccak-inplace.c // in the keccak reference code. var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64 - for i := 0; i < 24; i += 4 { + i := 0 + + if turbo { + i = 12 + } + + for ; i < 24; i += 4 { // Combines the 5 steps in each round into 2 steps. // Unrolls 4 rounds per loop and spreads some steps across rounds. diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go b/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go index b35cd006b03..a0df5aa6c59 100644 --- a/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go +++ b/vendor/github.com/cloudflare/circl/internal/sha3/sha3.go @@ -51,6 +51,7 @@ type State struct { // Specific to SHA-3 and SHAKE. outputLen int // the default output size in bytes state spongeDirection // whether the sponge is absorbing or squeezing + turbo bool // Whether we're using 12 rounds instead of 24 } // BlockSize returns the rate of sponge underlying this hash function. @@ -86,11 +87,11 @@ func (d *State) permute() { xorIn(d, d.buf()) d.bufe = 0 d.bufo = 0 - KeccakF1600(&d.a) + KeccakF1600(&d.a, d.turbo) case spongeSqueezing: // If we're squeezing, we need to apply the permutation before // copying more output. - KeccakF1600(&d.a) + KeccakF1600(&d.a, d.turbo) d.bufe = d.rate d.bufo = 0 copyOut(d, d.buf()) @@ -136,7 +137,7 @@ func (d *State) Write(p []byte) (written int, err error) { // The fast path; absorb a full "rate" bytes of input and apply the permutation. xorIn(d, p[:d.rate]) p = p[d.rate:] - KeccakF1600(&d.a) + KeccakF1600(&d.a, d.turbo) } else { // The slow path; buffer the input until we can fill the sponge, and then xor it in. todo := d.rate - bufl @@ -193,3 +194,7 @@ func (d *State) Sum(in []byte) []byte { _, _ = dup.Read(hash) return append(in, hash...) } + +func (d *State) IsAbsorbing() bool { + return d.state == spongeAbsorbing +} diff --git a/vendor/github.com/cloudflare/circl/internal/sha3/shake.go b/vendor/github.com/cloudflare/circl/internal/sha3/shake.go index b92c5b7d785..77817f758cb 100644 --- a/vendor/github.com/cloudflare/circl/internal/sha3/shake.go +++ b/vendor/github.com/cloudflare/circl/internal/sha3/shake.go @@ -57,6 +57,17 @@ func NewShake128() State { return State{rate: rate128, dsbyte: dsbyteShake} } +// NewTurboShake128 creates a new TurboSHAKE128 variable-output-length ShakeHash. +// Its generic security strength is 128 bits against all attacks if at +// least 32 bytes of its output are used. +// D is the domain separation byte and must be between 0x01 and 0x7f inclusive. +func NewTurboShake128(D byte) State { + if D == 0 || D > 0x7f { + panic("turboshake: D out of range") + } + return State{rate: rate128, dsbyte: D, turbo: true} +} + // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. // Its generic security strength is 256 bits against all attacks if // at least 64 bytes of its output are used. @@ -64,6 +75,17 @@ func NewShake256() State { return State{rate: rate256, dsbyte: dsbyteShake} } +// NewTurboShake256 creates a new TurboSHAKE256 variable-output-length ShakeHash. +// Its generic security strength is 256 bits against all attacks if +// at least 64 bytes of its output are used. +// D is the domain separation byte and must be between 0x01 and 0x7f inclusive. +func NewTurboShake256(D byte) State { + if D == 0 || D > 0x7f { + panic("turboshake: D out of range") + } + return State{rate: rate256, dsbyte: D, turbo: true} +} + // ShakeSum128 writes an arbitrary-length digest of data into hash. func ShakeSum128(hash, data []byte) { h := NewShake128() @@ -77,3 +99,21 @@ func ShakeSum256(hash, data []byte) { _, _ = h.Write(data) _, _ = h.Read(hash) } + +// TurboShakeSum128 writes an arbitrary-length digest of data into hash. +func TurboShakeSum128(hash, data []byte, D byte) { + h := NewTurboShake128(D) + _, _ = h.Write(data) + _, _ = h.Read(hash) +} + +// TurboShakeSum256 writes an arbitrary-length digest of data into hash. +func TurboShakeSum256(hash, data []byte, D byte) { + h := NewTurboShake256(D) + _, _ = h.Write(data) + _, _ = h.Read(hash) +} + +func (d *State) SwitchDS(D byte) { + d.dsbyte = D +} diff --git a/vendor/github.com/cloudflare/circl/math/primes.go b/vendor/github.com/cloudflare/circl/math/primes.go new file mode 100644 index 00000000000..158fd83a7aa --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/primes.go @@ -0,0 +1,34 @@ +package math + +import ( + "crypto/rand" + "io" + "math/big" +) + +// IsSafePrime reports whether p is (probably) a safe prime. +// The prime p=2*q+1 is safe prime if both p and q are primes. +// Note that ProbablyPrime is not suitable for judging primes +// that an adversary may have crafted to fool the test. +func IsSafePrime(p *big.Int) bool { + pdiv2 := new(big.Int).Rsh(p, 1) + return p.ProbablyPrime(20) && pdiv2.ProbablyPrime(20) +} + +// SafePrime returns a number of the given bit length that is a safe prime with high probability. +// The number returned p=2*q+1 is a safe prime if both p and q are primes. +// SafePrime will return error for any error returned by rand.Read or if bits < 2. +func SafePrime(random io.Reader, bits int) (*big.Int, error) { + one := big.NewInt(1) + p := new(big.Int) + for { + q, err := rand.Prime(random, bits-1) + if err != nil { + return nil, err + } + p.Lsh(q, 1).Add(p, one) + if p.ProbablyPrime(20) { + return p, nil + } + } +} diff --git a/vendor/github.com/cloudflare/circl/sign/ed25519/ed25519.go b/vendor/github.com/cloudflare/circl/sign/ed25519/ed25519.go index 08ca65d799a..2c73c26fb1f 100644 --- a/vendor/github.com/cloudflare/circl/sign/ed25519/ed25519.go +++ b/vendor/github.com/cloudflare/circl/sign/ed25519/ed25519.go @@ -1,7 +1,7 @@ // Package ed25519 implements Ed25519 signature scheme as described in RFC-8032. // // This package provides optimized implementations of the three signature -// variants and maintaining closer compatiblilty with crypto/ed25519. +// variants and maintaining closer compatibility with crypto/ed25519. // // | Scheme Name | Sign Function | Verification | Context | // |-------------|-------------------|---------------|-------------------| diff --git a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md index bbffea522e1..c1f280d4d0b 100644 --- a/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md +++ b/vendor/github.com/go-git/go-git/v5/COMPATIBILITY.md @@ -109,10 +109,10 @@ compatibility status with go-git. ## Server admin -| Feature | Sub-feature | Status | Notes | Examples | -| -------------------- | ----------- | ------ | ----- | -------- | -| `daemon` | | ❌ | | | -| `update-server-info` | | ❌ | | | +| Feature | Sub-feature | Status | Notes | Examples | +| -------------------- | ----------- | ------ | ----- | ----------------------------------------- | +| `daemon` | | ❌ | | | +| `update-server-info` | | ✅ | | [cli](./cli/go-git/update_server_info.go) | ## Advanced diff --git a/vendor/github.com/go-git/go-git/v5/config/branch.go b/vendor/github.com/go-git/go-git/v5/config/branch.go index 652270a2809..db2cb499acc 100644 --- a/vendor/github.com/go-git/go-git/v5/config/branch.go +++ b/vendor/github.com/go-git/go-git/v5/config/branch.go @@ -54,7 +54,7 @@ func (b *Branch) Validate() error { return errBranchInvalidRebase } - return nil + return plumbing.NewBranchReferenceName(b.Name).Validate() } func (b *Branch) marshal() *format.Subsection { diff --git a/vendor/github.com/go-git/go-git/v5/config/config.go b/vendor/github.com/go-git/go-git/v5/config/config.go index da425a78499..6d41c15dcdc 100644 --- a/vendor/github.com/go-git/go-git/v5/config/config.go +++ b/vendor/github.com/go-git/go-git/v5/config/config.go @@ -13,6 +13,7 @@ import ( "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5/internal/url" + "github.com/go-git/go-git/v5/plumbing" format "github.com/go-git/go-git/v5/plumbing/format/config" ) @@ -614,7 +615,7 @@ func (c *RemoteConfig) Validate() error { c.Fetch = []RefSpec{RefSpec(fmt.Sprintf(DefaultFetchRefSpec, c.Name))} } - return nil + return plumbing.NewRemoteHEADReferenceName(c.Name).Validate() } func (c *RemoteConfig) unmarshal(s *format.Subsection) error { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go index 2c7a33581e5..8898e5830e4 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go @@ -17,8 +17,11 @@ const ( s = 16 // https://github.com/git/git/blob/f7466e94375b3be27f229c78873f0acf8301c0a5/diff-delta.c#L428 - // Max size of a copy operation (64KB) + // Max size of a copy operation (64KB). maxCopySize = 64 * 1024 + + // Min size of a copy operation. + minCopySize = 4 ) // GetDelta returns an EncodedObject of type OFSDeltaObject. Base and Target object, diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go index edbc0e796b9..62f1d13cb8e 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go @@ -3,6 +3,7 @@ package packfile import ( "bytes" "errors" + "fmt" "io" "github.com/go-git/go-git/v5/plumbing" @@ -174,13 +175,25 @@ func (p *Parser) init() error { return nil } +type objectHeaderWriter func(typ plumbing.ObjectType, sz int64) error + +type lazyObjectWriter interface { + // LazyWriter enables an object to be lazily written. + // It returns: + // - w: a writer to receive the object's content. + // - lwh: a func to write the object header. + // - err: any error from the initial writer creation process. + // + // Note that if the object header is not written BEFORE the writer + // is used, this will result in an invalid object. + LazyWriter() (w io.WriteCloser, lwh objectHeaderWriter, err error) +} + func (p *Parser) indexObjects() error { buf := sync.GetBytesBuffer() defer sync.PutBytesBuffer(buf) for i := uint32(0); i < p.count; i++ { - buf.Reset() - oh, err := p.scanner.NextObjectHeader() if err != nil { return err @@ -220,21 +233,60 @@ func (p *Parser) indexObjects() error { ota = newBaseObject(oh.Offset, oh.Length, t) } - buf.Grow(int(oh.Length)) - _, crc, err := p.scanner.NextObject(buf) + hasher := plumbing.NewHasher(oh.Type, oh.Length) + writers := []io.Writer{hasher} + var obj *plumbing.MemoryObject + + // Lazy writing is only available for non-delta objects. + if p.storage != nil && !delta { + // When a storage is set and supports lazy writing, + // use that instead of creating a memory object. + if low, ok := p.storage.(lazyObjectWriter); ok { + ow, lwh, err := low.LazyWriter() + if err != nil { + return err + } + + if err = lwh(oh.Type, oh.Length); err != nil { + return err + } + + defer ow.Close() + writers = append(writers, ow) + } else { + obj = new(plumbing.MemoryObject) + obj.SetSize(oh.Length) + obj.SetType(oh.Type) + + writers = append(writers, obj) + } + } + if delta && !p.scanner.IsSeekable { + buf.Reset() + buf.Grow(int(oh.Length)) + writers = append(writers, buf) + } + + mw := io.MultiWriter(writers...) + + _, crc, err := p.scanner.NextObject(mw) if err != nil { return err } + // Non delta objects needs to be added into the storage. This + // is only required when lazy writing is not supported. + if obj != nil { + if _, err := p.storage.SetEncodedObject(obj); err != nil { + return err + } + } + ota.Crc32 = crc ota.Length = oh.Length - data := buf.Bytes() if !delta { - sha1, err := getSHA1(ota.Type, data) - if err != nil { - return err - } + sha1 := hasher.Sum() // Move children of placeholder parent into actual parent, in case this // was a non-external delta reference. @@ -249,20 +301,8 @@ func (p *Parser) indexObjects() error { p.oiByHash[ota.SHA1] = ota } - if p.storage != nil && !delta { - obj := new(plumbing.MemoryObject) - obj.SetSize(oh.Length) - obj.SetType(oh.Type) - if _, err := obj.Write(data); err != nil { - return err - } - - if _, err := p.storage.SetEncodedObject(obj); err != nil { - return err - } - } - if delta && !p.scanner.IsSeekable { + data := buf.Bytes() p.deltas[oh.Offset] = make([]byte, len(data)) copy(p.deltas[oh.Offset], data) } @@ -280,23 +320,29 @@ func (p *Parser) resolveDeltas() error { for _, obj := range p.oi { buf.Reset() + buf.Grow(int(obj.Length)) err := p.get(obj, buf) if err != nil { return err } - content := buf.Bytes() if err := p.onInflatedObjectHeader(obj.Type, obj.Length, obj.Offset); err != nil { return err } - if err := p.onInflatedObjectContent(obj.SHA1, obj.Offset, obj.Crc32, content); err != nil { + if err := p.onInflatedObjectContent(obj.SHA1, obj.Offset, obj.Crc32, nil); err != nil { return err } if !obj.IsDelta() && len(obj.Children) > 0 { + // Dealing with an io.ReaderAt object, means we can + // create it once and reuse across all children. + r := bytes.NewReader(buf.Bytes()) for _, child := range obj.Children { - if err := p.resolveObject(io.Discard, child, content); err != nil { + // Even though we are discarding the output, we still need to read it to + // so that the scanner can advance to the next object, and the SHA1 can be + // calculated. + if err := p.resolveObject(io.Discard, child, r); err != nil { return err } p.resolveExternalRef(child) @@ -361,13 +407,13 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) { if o.DiskType.IsDelta() { b := sync.GetBytesBuffer() defer sync.PutBytesBuffer(b) + buf.Grow(int(o.Length)) err := p.get(o.Parent, b) if err != nil { return err } - base := b.Bytes() - err = p.resolveObject(buf, o, base) + err = p.resolveObject(buf, o, bytes.NewReader(b.Bytes())) if err != nil { return err } @@ -378,6 +424,13 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) { } } + // If the scanner is seekable, caching this data into + // memory by offset seems wasteful. + // There is a trade-off to be considered here in terms + // of execution time vs memory consumption. + // + // TODO: improve seekable execution time, so that we can + // skip this cache. if len(o.Children) > 0 { data := make([]byte, buf.Len()) copy(data, buf.Bytes()) @@ -386,10 +439,25 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) { return nil } +// resolveObject resolves an object from base, using information +// provided by o. +// +// This call has the side-effect of changing field values +// from the object info o: +// - Type: OFSDeltaObject may become the target type (e.g. Blob). +// - Size: The size may be update with the target size. +// - Hash: Zero hashes will be calculated as part of the object +// resolution. Hence why this process can't be avoided even when w +// is an io.Discard. +// +// base must be an io.ReaderAt, which is a requirement from +// patchDeltaStream. The main reason being that reversing an +// delta object may lead to going backs and forths within base, +// which is not supported by io.Reader. func (p *Parser) resolveObject( w io.Writer, o *objectInfo, - base []byte, + base io.ReaderAt, ) error { if !o.DiskType.IsDelta() { return nil @@ -400,26 +468,46 @@ func (p *Parser) resolveObject( if err != nil { return err } - data := buf.Bytes() - data, err = applyPatchBase(o, data, base) + writers := []io.Writer{w} + var obj *plumbing.MemoryObject + var lwh objectHeaderWriter + + if p.storage != nil { + if low, ok := p.storage.(lazyObjectWriter); ok { + ow, wh, err := low.LazyWriter() + if err != nil { + return err + } + lwh = wh + + defer ow.Close() + writers = append(writers, ow) + } else { + obj = new(plumbing.MemoryObject) + ow, err := obj.Writer() + if err != nil { + return err + } + + writers = append(writers, ow) + } + } + + mw := io.MultiWriter(writers...) + + err = applyPatchBase(o, base, buf, mw, lwh) if err != nil { return err } - if p.storage != nil { - obj := new(plumbing.MemoryObject) - obj.SetSize(o.Size()) + if obj != nil { obj.SetType(o.Type) - if _, err := obj.Write(data); err != nil { - return err - } - + obj.SetSize(o.Size()) // Size here is correct as it was populated by applyPatchBase. if _, err := p.storage.SetEncodedObject(obj); err != nil { return err } } - _, err = w.Write(data) return err } @@ -443,24 +531,31 @@ func (p *Parser) readData(w io.Writer, o *objectInfo) error { return nil } -func applyPatchBase(ota *objectInfo, data, base []byte) ([]byte, error) { - patched, err := PatchDelta(base, data) - if err != nil { - return nil, err +// applyPatchBase applies the patch to target. +// +// Note that ota will be updated based on the description in resolveObject. +func applyPatchBase(ota *objectInfo, base io.ReaderAt, delta io.Reader, target io.Writer, wh objectHeaderWriter) error { + if target == nil { + return fmt.Errorf("cannot apply patch against nil target") } + typ := ota.Type if ota.SHA1 == plumbing.ZeroHash { - ota.Type = ota.Parent.Type - sha1, err := getSHA1(ota.Type, patched) - if err != nil { - return nil, err - } + typ = ota.Parent.Type + } + + sz, h, err := patchDeltaWriter(target, base, delta, typ, wh) + if err != nil { + return err + } - ota.SHA1 = sha1 - ota.Length = int64(len(patched)) + if ota.SHA1 == plumbing.ZeroHash { + ota.Type = typ + ota.Length = int64(sz) + ota.SHA1 = h } - return patched, nil + return nil } func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go index f00562d6325..960769c7c81 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "errors" + "fmt" "io" "math" @@ -17,7 +18,33 @@ import ( // and https://github.com/tarruda/node-git-core/blob/master/src/js/delta.js // for details about the delta format. -const deltaSizeMin = 4 +var ( + ErrInvalidDelta = errors.New("invalid delta") + ErrDeltaCmd = errors.New("wrong delta command") +) + +const ( + payload = 0x7f // 0111 1111 + continuation = 0x80 // 1000 0000 +) + +type offset struct { + mask byte + shift uint +} + +var offsets = []offset{ + {mask: 0x01, shift: 0}, + {mask: 0x02, shift: 8}, + {mask: 0x04, shift: 16}, + {mask: 0x08, shift: 24}, +} + +var sizes = []offset{ + {mask: 0x10, shift: 0}, + {mask: 0x20, shift: 8}, + {mask: 0x40, shift: 16}, +} // ApplyDelta writes to target the result of applying the modification deltas in delta to base. func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) { @@ -58,11 +85,6 @@ func ApplyDelta(target, base plumbing.EncodedObject, delta []byte) (err error) { return err } -var ( - ErrInvalidDelta = errors.New("invalid delta") - ErrDeltaCmd = errors.New("wrong delta command") -) - // PatchDelta returns the result of applying the modification deltas in delta to src. // An error will be returned if delta is corrupted (ErrDeltaLen) or an action command // is not copy from source or copy from delta (ErrDeltaCmd). @@ -120,7 +142,8 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo return } - if isCopyFromSrc(cmd) { + switch { + case isCopyFromSrc(cmd): offset, err := decodeOffsetByteReader(cmd, deltaBuf) if err != nil { _ = dstWr.CloseWithError(err) @@ -173,7 +196,8 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo } remainingTargetSz -= sz basePos += sz - } else if isCopyFromDelta(cmd) { + + case isCopyFromDelta(cmd): sz := uint(cmd) // cmd is the size itself if invalidSize(sz, targetSz) { _ = dstWr.CloseWithError(ErrInvalidDelta) @@ -185,10 +209,12 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo } remainingTargetSz -= sz - } else { + + default: _ = dstWr.CloseWithError(ErrDeltaCmd) return } + if remainingTargetSz <= 0 { _ = dstWr.Close() return @@ -200,7 +226,7 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo } func patchDelta(dst *bytes.Buffer, src, delta []byte) error { - if len(delta) < deltaSizeMin { + if len(delta) < minCopySize { return ErrInvalidDelta } @@ -221,7 +247,9 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { cmd = delta[0] delta = delta[1:] - if isCopyFromSrc(cmd) { + + switch { + case isCopyFromSrc(cmd): var offset, sz uint var err error offset, delta, err = decodeOffset(cmd, delta) @@ -240,7 +268,8 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { } dst.Write(src[offset : offset+sz]) remainingTargetSz -= sz - } else if isCopyFromDelta(cmd) { + + case isCopyFromDelta(cmd): sz := uint(cmd) // cmd is the size itself if invalidSize(sz, targetSz) { return ErrInvalidDelta @@ -253,7 +282,8 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { dst.Write(delta[0:sz]) remainingTargetSz -= sz delta = delta[sz:] - } else { + + default: return ErrDeltaCmd } @@ -265,6 +295,107 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { return nil } +func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader, + typ plumbing.ObjectType, writeHeader objectHeaderWriter) (uint, plumbing.Hash, error) { + deltaBuf := bufio.NewReaderSize(delta, 1024) + srcSz, err := decodeLEB128ByteReader(deltaBuf) + if err != nil { + if err == io.EOF { + return 0, plumbing.ZeroHash, ErrInvalidDelta + } + return 0, plumbing.ZeroHash, err + } + + if r, ok := base.(*bytes.Reader); ok && srcSz != uint(r.Size()) { + return 0, plumbing.ZeroHash, ErrInvalidDelta + } + + targetSz, err := decodeLEB128ByteReader(deltaBuf) + if err != nil { + if err == io.EOF { + return 0, plumbing.ZeroHash, ErrInvalidDelta + } + return 0, plumbing.ZeroHash, err + } + + // If header still needs to be written, caller will provide + // a LazyObjectWriterHeader. This seems to be the case when + // dealing with thin-packs. + if writeHeader != nil { + err = writeHeader(typ, int64(targetSz)) + if err != nil { + return 0, plumbing.ZeroHash, fmt.Errorf("could not lazy write header: %w", err) + } + } + + remainingTargetSz := targetSz + + hasher := plumbing.NewHasher(typ, int64(targetSz)) + mw := io.MultiWriter(dst, hasher) + + bufp := sync.GetByteSlice() + defer sync.PutByteSlice(bufp) + + sr := io.NewSectionReader(base, int64(0), int64(srcSz)) + // Keep both the io.LimitedReader types, so we can reset N. + baselr := io.LimitReader(sr, 0).(*io.LimitedReader) + deltalr := io.LimitReader(deltaBuf, 0).(*io.LimitedReader) + + for { + buf := *bufp + cmd, err := deltaBuf.ReadByte() + if err == io.EOF { + return 0, plumbing.ZeroHash, ErrInvalidDelta + } + if err != nil { + return 0, plumbing.ZeroHash, err + } + + if isCopyFromSrc(cmd) { + offset, err := decodeOffsetByteReader(cmd, deltaBuf) + if err != nil { + return 0, plumbing.ZeroHash, err + } + sz, err := decodeSizeByteReader(cmd, deltaBuf) + if err != nil { + return 0, plumbing.ZeroHash, err + } + + if invalidSize(sz, targetSz) || + invalidOffsetSize(offset, sz, srcSz) { + return 0, plumbing.ZeroHash, err + } + + if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil { + return 0, plumbing.ZeroHash, err + } + baselr.N = int64(sz) + if _, err := io.CopyBuffer(mw, baselr, buf); err != nil { + return 0, plumbing.ZeroHash, err + } + remainingTargetSz -= sz + } else if isCopyFromDelta(cmd) { + sz := uint(cmd) // cmd is the size itself + if invalidSize(sz, targetSz) { + return 0, plumbing.ZeroHash, ErrInvalidDelta + } + deltalr.N = int64(sz) + if _, err := io.CopyBuffer(mw, deltalr, buf); err != nil { + return 0, plumbing.ZeroHash, err + } + + remainingTargetSz -= sz + } else { + return 0, plumbing.ZeroHash, err + } + if remainingTargetSz <= 0 { + break + } + } + + return targetSz, hasher.Sum(), nil +} + // Decodes a number encoded as an unsigned LEB128 at the start of some // binary data and returns the decoded number and the rest of the // stream. @@ -306,48 +437,24 @@ func decodeLEB128ByteReader(input io.ByteReader) (uint, error) { return num, nil } -const ( - payload = 0x7f // 0111 1111 - continuation = 0x80 // 1000 0000 -) - func isCopyFromSrc(cmd byte) bool { - return (cmd & 0x80) != 0 + return (cmd & continuation) != 0 } func isCopyFromDelta(cmd byte) bool { - return (cmd&0x80) == 0 && cmd != 0 + return (cmd&continuation) == 0 && cmd != 0 } func decodeOffsetByteReader(cmd byte, delta io.ByteReader) (uint, error) { var offset uint - if (cmd & 0x01) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err - } - offset = uint(next) - } - if (cmd & 0x02) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err - } - offset |= uint(next) << 8 - } - if (cmd & 0x04) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err - } - offset |= uint(next) << 16 - } - if (cmd & 0x08) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err + for _, o := range offsets { + if (cmd & o.mask) != 0 { + next, err := delta.ReadByte() + if err != nil { + return 0, err + } + offset |= uint(next) << o.shift } - offset |= uint(next) << 24 } return offset, nil @@ -355,33 +462,14 @@ func decodeOffsetByteReader(cmd byte, delta io.ByteReader) (uint, error) { func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) { var offset uint - if (cmd & 0x01) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta - } - offset = uint(delta[0]) - delta = delta[1:] - } - if (cmd & 0x02) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta - } - offset |= uint(delta[0]) << 8 - delta = delta[1:] - } - if (cmd & 0x04) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta - } - offset |= uint(delta[0]) << 16 - delta = delta[1:] - } - if (cmd & 0x08) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta + for _, o := range offsets { + if (cmd & o.mask) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + offset |= uint(delta[0]) << o.shift + delta = delta[1:] } - offset |= uint(delta[0]) << 24 - delta = delta[1:] } return offset, delta, nil @@ -389,29 +477,18 @@ func decodeOffset(cmd byte, delta []byte) (uint, []byte, error) { func decodeSizeByteReader(cmd byte, delta io.ByteReader) (uint, error) { var sz uint - if (cmd & 0x10) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err - } - sz = uint(next) - } - if (cmd & 0x20) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err - } - sz |= uint(next) << 8 - } - if (cmd & 0x40) != 0 { - next, err := delta.ReadByte() - if err != nil { - return 0, err + for _, s := range sizes { + if (cmd & s.mask) != 0 { + next, err := delta.ReadByte() + if err != nil { + return 0, err + } + sz |= uint(next) << s.shift } - sz |= uint(next) << 16 } + if sz == 0 { - sz = 0x10000 + sz = maxCopySize } return sz, nil @@ -419,29 +496,17 @@ func decodeSizeByteReader(cmd byte, delta io.ByteReader) (uint, error) { func decodeSize(cmd byte, delta []byte) (uint, []byte, error) { var sz uint - if (cmd & 0x10) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta - } - sz = uint(delta[0]) - delta = delta[1:] - } - if (cmd & 0x20) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta - } - sz |= uint(delta[0]) << 8 - delta = delta[1:] - } - if (cmd & 0x40) != 0 { - if len(delta) == 0 { - return 0, nil, ErrInvalidDelta + for _, s := range sizes { + if (cmd & s.mask) != 0 { + if len(delta) == 0 { + return 0, nil, ErrInvalidDelta + } + sz |= uint(delta[0]) << s.shift + delta = delta[1:] } - sz |= uint(delta[0]) << 16 - delta = delta[1:] } if sz == 0 { - sz = 0x10000 + sz = maxCopySize } return sz, delta, nil diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/encoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/encoder.go index 6d409795b0e..b6144faf584 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/encoder.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/encoder.go @@ -7,6 +7,8 @@ import ( "errors" "fmt" "io" + + "github.com/go-git/go-git/v5/utils/trace" ) // An Encoder writes pkt-lines to an output stream. @@ -43,6 +45,7 @@ func NewEncoder(w io.Writer) *Encoder { // Flush encodes a flush-pkt to the output stream. func (e *Encoder) Flush() error { + defer trace.Packet.Print("packet: > 0000") _, err := e.w.Write(FlushPkt) return err } @@ -70,6 +73,7 @@ func (e *Encoder) encodeLine(p []byte) error { } n := len(p) + 4 + defer trace.Packet.Printf("packet: > %04x %s", n, p) if _, err := e.w.Write(asciiHex16(n)); err != nil { return err } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/error.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/error.go new file mode 100644 index 00000000000..2c0e5a72a9b --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/error.go @@ -0,0 +1,51 @@ +package pktline + +import ( + "bytes" + "errors" + "io" + "strings" +) + +var ( + // ErrInvalidErrorLine is returned by Decode when the packet line is not an + // error line. + ErrInvalidErrorLine = errors.New("expected an error-line") + + errPrefix = []byte("ERR ") +) + +// ErrorLine is a packet line that contains an error message. +// Once this packet is sent by client or server, the data transfer process is +// terminated. +// See https://git-scm.com/docs/pack-protocol#_pkt_line_format +type ErrorLine struct { + Text string +} + +// Error implements the error interface. +func (e *ErrorLine) Error() string { + return e.Text +} + +// Encode encodes the ErrorLine into a packet line. +func (e *ErrorLine) Encode(w io.Writer) error { + p := NewEncoder(w) + return p.Encodef("%s%s\n", string(errPrefix), e.Text) +} + +// Decode decodes a packet line into an ErrorLine. +func (e *ErrorLine) Decode(r io.Reader) error { + s := NewScanner(r) + if !s.Scan() { + return s.Err() + } + + line := s.Bytes() + if !bytes.HasPrefix(line, errPrefix) { + return ErrInvalidErrorLine + } + + e.Text = strings.TrimSpace(string(line[4:])) + return nil +} diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go index 99aab46e88d..fbb137de06b 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/pktline/scanner.go @@ -1,8 +1,12 @@ package pktline import ( + "bytes" "errors" "io" + "strings" + + "github.com/go-git/go-git/v5/utils/trace" ) const ( @@ -65,6 +69,14 @@ func (s *Scanner) Scan() bool { return false } s.payload = s.payload[:l] + trace.Packet.Printf("packet: < %04x %s", l, s.payload) + + if bytes.HasPrefix(s.payload, errPrefix) { + s.err = &ErrorLine{ + Text: strings.TrimSpace(string(s.payload[4:])), + } + return false + } return true } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go index 06bc35bbcc8..dd8fef44783 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/patch.go @@ -317,8 +317,8 @@ func getFileStatsFromFilePatches(filePatches []fdiff.FilePatch) FileStats { // File is deleted. cs.Name = from.Path() } else if from.Path() != to.Path() { - // File is renamed. Not supported. - // cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path()) + // File is renamed. + cs.Name = fmt.Sprintf("%s => %s", from.Path(), to.Path()) } else { cs.Name = from.Path() } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/common.go index fef50a45098..a858323e79b 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/common.go @@ -48,6 +48,11 @@ func isFlush(payload []byte) bool { return len(payload) == 0 } +var ( + // ErrNilWriter is returned when a nil writer is passed to the encoder. + ErrNilWriter = fmt.Errorf("nil writer") +) + // ErrUnexpectedData represents an unexpected data decoding a message type ErrUnexpectedData struct { Msg string diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/gitproto.go b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/gitproto.go new file mode 100644 index 00000000000..0b7ff8f8260 --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/gitproto.go @@ -0,0 +1,120 @@ +package packp + +import ( + "fmt" + "io" + "strings" + + "github.com/go-git/go-git/v5/plumbing/format/pktline" +) + +var ( + // ErrInvalidGitProtoRequest is returned by Decode if the input is not a + // valid git protocol request. + ErrInvalidGitProtoRequest = fmt.Errorf("invalid git protocol request") +) + +// GitProtoRequest is a command request for the git protocol. +// It is used to send the command, endpoint, and extra parameters to the +// remote. +// See https://git-scm.com/docs/pack-protocol#_git_transport +type GitProtoRequest struct { + RequestCommand string + Pathname string + + // Optional + Host string + + // Optional + ExtraParams []string +} + +// validate validates the request. +func (g *GitProtoRequest) validate() error { + if g.RequestCommand == "" { + return fmt.Errorf("%w: empty request command", ErrInvalidGitProtoRequest) + } + + if g.Pathname == "" { + return fmt.Errorf("%w: empty pathname", ErrInvalidGitProtoRequest) + } + + return nil +} + +// Encode encodes the request into the writer. +func (g *GitProtoRequest) Encode(w io.Writer) error { + if w == nil { + return ErrNilWriter + } + + if err := g.validate(); err != nil { + return err + } + + p := pktline.NewEncoder(w) + req := fmt.Sprintf("%s %s\x00", g.RequestCommand, g.Pathname) + if host := g.Host; host != "" { + req += fmt.Sprintf("host=%s\x00", host) + } + + if len(g.ExtraParams) > 0 { + req += "\x00" + for _, param := range g.ExtraParams { + req += param + "\x00" + } + } + + if err := p.Encode([]byte(req)); err != nil { + return err + } + + return nil +} + +// Decode decodes the request from the reader. +func (g *GitProtoRequest) Decode(r io.Reader) error { + s := pktline.NewScanner(r) + if !s.Scan() { + err := s.Err() + if err == nil { + return ErrInvalidGitProtoRequest + } + return err + } + + line := string(s.Bytes()) + if len(line) == 0 { + return io.EOF + } + + if line[len(line)-1] != 0 { + return fmt.Errorf("%w: missing null terminator", ErrInvalidGitProtoRequest) + } + + parts := strings.SplitN(line, " ", 2) + if len(parts) != 2 { + return fmt.Errorf("%w: short request", ErrInvalidGitProtoRequest) + } + + g.RequestCommand = parts[0] + params := strings.Split(parts[1], string(null)) + if len(params) < 1 { + return fmt.Errorf("%w: missing pathname", ErrInvalidGitProtoRequest) + } + + g.Pathname = params[0] + if len(params) > 1 { + g.Host = strings.TrimPrefix(params[1], "host=") + } + + if len(params) > 2 { + for _, param := range params[2:] { + if param != "" { + g.ExtraParams = append(g.ExtraParams, param) + } + } + } + + return nil +} diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/reference.go b/vendor/github.com/go-git/go-git/v5/plumbing/reference.go index 5a67f69e72a..ddba930292d 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/reference.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/reference.go @@ -3,6 +3,7 @@ package plumbing import ( "errors" "fmt" + "regexp" "strings" ) @@ -29,6 +30,9 @@ var RefRevParseRules = []string{ var ( ErrReferenceNotFound = errors.New("reference not found") + + // ErrInvalidReferenceName is returned when a reference name is invalid. + ErrInvalidReferenceName = errors.New("invalid reference name") ) // ReferenceType reference type's @@ -124,6 +128,91 @@ func (r ReferenceName) Short() string { return res } +var ( + ctrlSeqs = regexp.MustCompile(`[\000-\037\177]`) +) + +// Validate validates a reference name. +// This follows the git-check-ref-format rules. +// See https://git-scm.com/docs/git-check-ref-format +// +// It is important to note that this function does not check if the reference +// exists in the repository. +// It only checks if the reference name is valid. +// This functions does not support the --refspec-pattern, --normalize, and +// --allow-onelevel options. +// +// Git imposes the following rules on how references are named: +// +// 1. They can include slash / for hierarchical (directory) grouping, but no +// slash-separated component can begin with a dot . or end with the +// sequence .lock. +// 2. They must contain at least one /. This enforces the presence of a +// category like heads/, tags/ etc. but the actual names are not +// restricted. If the --allow-onelevel option is used, this rule is +// waived. +// 3. They cannot have two consecutive dots .. anywhere. +// 4. They cannot have ASCII control characters (i.e. bytes whose values are +// lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : +// anywhere. +// 5. They cannot have question-mark ?, asterisk *, or open bracket [ +// anywhere. See the --refspec-pattern option below for an exception to this +// rule. +// 6. They cannot begin or end with a slash / or contain multiple consecutive +// slashes (see the --normalize option below for an exception to this rule). +// 7. They cannot end with a dot .. +// 8. They cannot contain a sequence @{. +// 9. They cannot be the single character @. +// 10. They cannot contain a \. +func (r ReferenceName) Validate() error { + s := string(r) + if len(s) == 0 { + return ErrInvalidReferenceName + } + + // HEAD is a special case + if r == HEAD { + return nil + } + + // rule 7 + if strings.HasSuffix(s, ".") { + return ErrInvalidReferenceName + } + + // rule 2 + parts := strings.Split(s, "/") + if len(parts) < 2 { + return ErrInvalidReferenceName + } + + isBranch := r.IsBranch() + isTag := r.IsTag() + for _, part := range parts { + // rule 6 + if len(part) == 0 { + return ErrInvalidReferenceName + } + + if strings.HasPrefix(part, ".") || // rule 1 + strings.Contains(part, "..") || // rule 3 + ctrlSeqs.MatchString(part) || // rule 4 + strings.ContainsAny(part, "~^:?*[ \t\n") || // rule 4 & 5 + strings.Contains(part, "@{") || // rule 8 + part == "@" || // rule 9 + strings.Contains(part, "\\") || // rule 10 + strings.HasSuffix(part, ".lock") { // rule 1 + return ErrInvalidReferenceName + } + + if (isBranch || isTag) && strings.HasPrefix(part, "-") { // branches & tags can't start with - + return ErrInvalidReferenceName + } + } + + return nil +} + const ( HEAD ReferenceName = "HEAD" Master ReferenceName = "refs/heads/master" diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go index 6f0a38012b5..38714e2ad1c 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/file/client.go @@ -11,7 +11,6 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/internal/common" - "github.com/go-git/go-git/v5/utils/ioutil" "golang.org/x/sys/execabs" ) @@ -112,7 +111,7 @@ func (c *command) Start() error { func (c *command) StderrPipe() (io.Reader, error) { // Pipe returned by Command.StderrPipe has a race with Read + Command.Wait. // We use an io.Pipe and close it after the command finishes. - r, w := ioutil.Pipe() + r, w := io.Pipe() c.cmd.Stderr = w c.stderrCloser = r return r, nil diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/git/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/git/common.go index 92fc0becc05..2b878b0359e 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/git/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/git/common.go @@ -2,12 +2,11 @@ package git import ( - "fmt" "io" "net" "strconv" - "github.com/go-git/go-git/v5/plumbing/format/pktline" + "github.com/go-git/go-git/v5/plumbing/protocol/packp" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/internal/common" "github.com/go-git/go-git/v5/utils/ioutil" @@ -42,10 +41,18 @@ type command struct { // Start executes the command sending the required message to the TCP connection func (c *command) Start() error { - cmd := endpointToCommand(c.command, c.endpoint) + req := packp.GitProtoRequest{ + RequestCommand: c.command, + Pathname: c.endpoint.Path, + } + host := c.endpoint.Host + if c.endpoint.Port != DefaultPort { + host = net.JoinHostPort(c.endpoint.Host, strconv.Itoa(c.endpoint.Port)) + } + + req.Host = host - e := pktline.NewEncoder(c.conn) - return e.Encode([]byte(cmd)) + return req.Encode(c.conn) } func (c *command) connect() error { @@ -90,15 +97,6 @@ func (c *command) StdoutPipe() (io.Reader, error) { return c.conn, nil } -func endpointToCommand(cmd string, ep *transport.Endpoint) string { - host := ep.Host - if ep.Port != DefaultPort { - host = net.JoinHostPort(ep.Host, strconv.Itoa(ep.Port)) - } - - return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path, 0, host, 0) -} - // Close closes the TCP connection and connection. func (c *command) Close() error { if !c.connected { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go index 6574116b18f..9e1d02357f2 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/internal/common/common.go @@ -203,9 +203,22 @@ func (s *session) AdvertisedReferencesContext(ctx context.Context) (*packp.AdvRe } func (s *session) handleAdvRefDecodeError(err error) error { + var errLine *pktline.ErrorLine + if errors.As(err, &errLine) { + if isRepoNotFoundError(errLine.Text) { + return transport.ErrRepositoryNotFound + } + + return errLine + } + // If repository is not found, we get empty stdout and server writes an // error to stderr. - if err == packp.ErrEmptyInput { + if errors.Is(err, packp.ErrEmptyInput) { + // TODO:(v6): handle this error in a better way. + // Instead of checking the stderr output for a specific error message, + // define an ExitError and embed the stderr output and exit (if one + // exists) in the error struct. Just like exec.ExitError. s.finished = true if err := s.checkNotFoundError(); err != nil { return err @@ -245,6 +258,12 @@ func (s *session) handleAdvRefDecodeError(err error) error { // returned with the packfile content. The reader must be closed after reading. func (s *session) UploadPack(ctx context.Context, req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) { if req.IsEmpty() { + // XXX: IsEmpty means haves are a subset of wants, in that case we have + // everything we asked for. Close the connection and return nil. + if err := s.finish(); err != nil { + return nil, err + } + // TODO:(v6) return nil here return nil, transport.ErrEmptyUploadPackRequest } @@ -393,59 +412,43 @@ func (s *session) checkNotFoundError() error { return transport.ErrRepositoryNotFound } + // TODO:(v6): return server error just as it is without a prefix return fmt.Errorf("unknown error: %s", line) } } -var ( - githubRepoNotFoundErr = "ERROR: Repository not found." - bitbucketRepoNotFoundErr = "conq: repository does not exist." +const ( + githubRepoNotFoundErr = "Repository not found." + bitbucketRepoNotFoundErr = "repository does not exist." localRepoNotFoundErr = "does not appear to be a git repository" - gitProtocolNotFoundErr = "ERR \n Repository not found." - gitProtocolNoSuchErr = "ERR no such repository" - gitProtocolAccessDeniedErr = "ERR access denied" - gogsAccessDeniedErr = "Gogs: Repository does not exist or you do not have access" - gitlabRepoNotFoundErr = "remote: ERROR: The project you were looking for could not be found" + gitProtocolNotFoundErr = "Repository not found." + gitProtocolNoSuchErr = "no such repository" + gitProtocolAccessDeniedErr = "access denied" + gogsAccessDeniedErr = "Repository does not exist or you do not have access" + gitlabRepoNotFoundErr = "The project you were looking for could not be found" ) func isRepoNotFoundError(s string) bool { - if strings.HasPrefix(s, githubRepoNotFoundErr) { - return true - } - - if strings.HasPrefix(s, bitbucketRepoNotFoundErr) { - return true - } - - if strings.HasSuffix(s, localRepoNotFoundErr) { - return true - } - - if strings.HasPrefix(s, gitProtocolNotFoundErr) { - return true - } - - if strings.HasPrefix(s, gitProtocolNoSuchErr) { - return true - } - - if strings.HasPrefix(s, gitProtocolAccessDeniedErr) { - return true - } - - if strings.HasPrefix(s, gogsAccessDeniedErr) { - return true - } - - if strings.HasPrefix(s, gitlabRepoNotFoundErr) { - return true + for _, err := range []string{ + githubRepoNotFoundErr, + bitbucketRepoNotFoundErr, + localRepoNotFoundErr, + gitProtocolNotFoundErr, + gitProtocolNoSuchErr, + gitProtocolAccessDeniedErr, + gogsAccessDeniedErr, + gitlabRepoNotFoundErr, + } { + if strings.Contains(s, err) { + return true + } } return false } // uploadPack implements the git-upload-pack protocol. -func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error { +func uploadPack(w io.WriteCloser, _ io.Reader, req *packp.UploadPackRequest) error { // TODO support multi_ack mode // TODO support multi_ack_detailed mode // TODO support acks for common objects diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go index 11fa0c80135..cf5d6f43feb 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/server/server.go @@ -166,7 +166,7 @@ func (s *upSession) UploadPack(ctx context.Context, req *packp.UploadPackRequest return nil, err } - pr, pw := ioutil.Pipe() + pr, pw := io.Pipe() e := packfile.NewEncoder(pw, s.storer, false) go func() { // TODO: plumb through a pack window. diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go index 15316038b35..46fda73fa41 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go @@ -168,7 +168,7 @@ func dial(network, addr string, proxyOpts transport.ProxyOptions, config *ssh.Cl defer cancel() var conn net.Conn - var err error + var dialErr error if proxyOpts.URL != "" { proxyUrl, err := proxyOpts.FullURL() @@ -186,12 +186,12 @@ func dial(network, addr string, proxyOpts transport.ProxyOptions, config *ssh.Cl return nil, fmt.Errorf("expected ssh proxy dialer to be of type %s; got %s", reflect.TypeOf(ctxDialer), reflect.TypeOf(dialer)) } - conn, err = ctxDialer.DialContext(ctx, "tcp", addr) + conn, dialErr = ctxDialer.DialContext(ctx, "tcp", addr) } else { - conn, err = proxy.Dial(ctx, network, addr) + conn, dialErr = proxy.Dial(ctx, network, addr) } - if err != nil { - return nil, err + if dialErr != nil { + return nil, dialErr } c, chans, reqs, err := ssh.NewClientConn(conn, addr, config) diff --git a/vendor/github.com/go-git/go-git/v5/remote.go b/vendor/github.com/go-git/go-git/v5/remote.go index 2ffffe7b6df..0cb70bc0093 100644 --- a/vendor/github.com/go-git/go-git/v5/remote.go +++ b/vendor/github.com/go-git/go-git/v5/remote.go @@ -552,6 +552,10 @@ func (r *Remote) fetchPack(ctx context.Context, o *FetchOptions, s transport.Upl reader, err := s.UploadPack(ctx, req) if err != nil { + if errors.Is(err, transport.ErrEmptyUploadPackRequest) { + // XXX: no packfile provided, everything is up-to-date. + return nil + } return err } @@ -1066,7 +1070,7 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String()) } - ff, err := isFastForward(s, cmd.Old, cmd.New) + ff, err := isFastForward(s, cmd.Old, cmd.New, nil) if err != nil { return err } @@ -1078,14 +1082,28 @@ func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.Refe return nil } -func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) { +func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash, earliestShallow *plumbing.Hash) (bool, error) { c, err := object.GetCommit(s, new) if err != nil { return false, err } + parentsToIgnore := []plumbing.Hash{} + if earliestShallow != nil { + earliestCommit, err := object.GetCommit(s, *earliestShallow) + if err != nil { + return false, err + } + + parentsToIgnore = earliestCommit.ParentHashes + } + found := false - iter := object.NewCommitPreorderIter(c, nil, nil) + // stop iterating at the earlist shallow commit, ignoring its parents + // note: when pull depth is smaller than the number of new changes on the remote, this fails due to missing parents. + // as far as i can tell, without the commits in-between the shallow pull and the earliest shallow, there's no + // real way of telling whether it will be a fast-forward merge. + iter := object.NewCommitPreorderIter(c, nil, parentsToIgnore) err = iter.ForEach(func(c *object.Commit) error { if c.Hash != old { return nil @@ -1198,10 +1216,10 @@ func (r *Remote) updateLocalReferenceStorage( old, _ := storer.ResolveReference(r.s, localName) new := plumbing.NewHashReference(localName, ref.Hash()) - // If the ref exists locally as a branch and force is not specified, - // only update if the new ref is an ancestor of the old - if old != nil && old.Name().IsBranch() && !force && !spec.IsForceUpdate() { - ff, err := isFastForward(r.s, old.Hash(), new.Hash()) + // If the ref exists locally as a non-tag and force is not + // specified, only update if the new ref is an ancestor of the old + if old != nil && !old.Name().IsTag() && !force && !spec.IsForceUpdate() { + ff, err := isFastForward(r.s, old.Hash(), new.Hash(), nil) if err != nil { return updated, err } @@ -1386,8 +1404,7 @@ func pushHashes( useRefDeltas bool, allDelete bool, ) (*packp.ReportStatus, error) { - - rd, wr := ioutil.Pipe() + rd, wr := io.Pipe() config, err := s.Config() if err != nil { diff --git a/vendor/github.com/go-git/go-git/v5/repository.go b/vendor/github.com/go-git/go-git/v5/repository.go index 48988383dd0..1524a691305 100644 --- a/vendor/github.com/go-git/go-git/v5/repository.go +++ b/vendor/github.com/go-git/go-git/v5/repository.go @@ -98,6 +98,10 @@ func InitWithOptions(s storage.Storer, worktree billy.Filesystem, options InitOp options.DefaultBranch = plumbing.Master } + if err := options.DefaultBranch.Validate(); err != nil { + return nil, err + } + r := newRepository(s, worktree) _, err := r.Reference(plumbing.HEAD, false) switch err { @@ -724,7 +728,10 @@ func (r *Repository) DeleteBranch(name string) error { // CreateTag creates a tag. If opts is included, the tag is an annotated tag, // otherwise a lightweight tag is created. func (r *Repository) CreateTag(name string, hash plumbing.Hash, opts *CreateTagOptions) (*plumbing.Reference, error) { - rname := plumbing.ReferenceName(path.Join("refs", "tags", name)) + rname := plumbing.NewTagReferenceName(name) + if err := rname.Validate(); err != nil { + return nil, err + } _, err := r.Storer.Reference(rname) switch err { diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go index 3080e4accbd..31c46948164 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go @@ -10,18 +10,19 @@ import ( "os" "path" "path/filepath" + "reflect" "runtime" "sort" "strings" "time" - "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/hash" "github.com/go-git/go-git/v5/storage" "github.com/go-git/go-git/v5/utils/ioutil" "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/helper/chroot" ) const ( @@ -81,6 +82,10 @@ type Options struct { // KeepDescriptors makes the file descriptors to be reused but they will // need to be manually closed calling Close(). KeepDescriptors bool + // AlternatesFS provides the billy filesystem to be used for Git Alternates. + // If none is provided, it falls back to using the underlying instance used for + // DotGit. + AlternatesFS billy.Filesystem } // The DotGit type represents a local git repository on disk. This @@ -1146,28 +1151,55 @@ func (d *DotGit) Alternates() ([]*DotGit, error) { } defer f.Close() + fs := d.options.AlternatesFS + if fs == nil { + fs = d.fs + } + var alternates []*DotGit + seen := make(map[string]struct{}) // Read alternate paths line-by-line and create DotGit objects. scanner := bufio.NewScanner(f) for scanner.Scan() { path := scanner.Text() - if !filepath.IsAbs(path) { - // For relative paths, we can perform an internal conversion to - // slash so that they work cross-platform. - slashPath := filepath.ToSlash(path) - // If the path is not absolute, it must be relative to object - // database (.git/objects/info). - // https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html - // Hence, derive a path relative to DotGit's root. - // "../../../reponame/.git/" -> "../../reponame/.git" - // Remove the first ../ - relpath := filepath.Join(strings.Split(slashPath, "/")[1:]...) - normalPath := filepath.FromSlash(relpath) - path = filepath.Join(d.fs.Root(), normalPath) + + // Avoid creating multiple dotgits for the same alternative path. + if _, ok := seen[path]; ok { + continue + } + + seen[path] = struct{}{} + + if filepath.IsAbs(path) { + // Handling absolute paths should be straight-forward. However, the default osfs (Chroot) + // tries to concatenate an abs path with the root path in some operations (e.g. Stat), + // which leads to unexpected errors. Therefore, make the path relative to the current FS instead. + if reflect.TypeOf(fs) == reflect.TypeOf(&chroot.ChrootHelper{}) { + path, err = filepath.Rel(fs.Root(), path) + if err != nil { + return nil, fmt.Errorf("cannot make path %q relative: %w", path, err) + } + } + } else { + // By Git conventions, relative paths should be based on the object database (.git/objects/info) + // location as per: https://www.kernel.org/pub/software/scm/git/docs/gitrepository-layout.html + // However, due to the nature of go-git and its filesystem handling via Billy, paths cannot + // cross its "chroot boundaries". Therefore, ignore any "../" and treat the path from the + // fs root. If this is not correct based on the dotgit fs, set a different one via AlternatesFS. + abs := filepath.Join(string(filepath.Separator), filepath.ToSlash(path)) + path = filepath.FromSlash(abs) + } + + // Aligns with upstream behavior: exit if target path is not a valid directory. + if fi, err := fs.Stat(path); err != nil || !fi.IsDir() { + return nil, fmt.Errorf("invalid object directory %q: %w", path, err) + } + afs, err := fs.Chroot(filepath.Dir(path)) + if err != nil { + return nil, fmt.Errorf("cannot chroot %q: %w", path, err) } - fs := osfs.New(filepath.Dir(path)) - alternates = append(alternates, New(fs)) + alternates = append(alternates, New(afs)) } if err = scanner.Err(); err != nil { diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go index 846a7b86020..e812fe934d8 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go @@ -146,6 +146,19 @@ func (s *ObjectStorage) SetEncodedObject(o plumbing.EncodedObject) (h plumbing.H return o.Hash(), err } +// LazyWriter returns a lazy ObjectWriter that is bound to a DotGit file. +// It first write the header passing on the object type and size, so +// that the object contents can be written later, without the need to +// create a MemoryObject and buffering its entire contents into memory. +func (s *ObjectStorage) LazyWriter() (w io.WriteCloser, wh func(typ plumbing.ObjectType, sz int64) error, err error) { + ow, err := s.dir.NewObject() + if err != nil { + return nil, nil, err + } + + return ow, ow.WriteHeader, nil +} + // HasEncodedObject returns nil if the object exists, without actually // reading the object data from storage. func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) { diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/storage.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/storage.go index 2069d3a6f3f..951ea00c831 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/storage.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/storage.go @@ -37,6 +37,10 @@ type Options struct { // LargeObjectThreshold maximum object size (in bytes) that will be read in to memory. // If left unset or set to 0 there is no limit LargeObjectThreshold int64 + // AlternatesFS provides the billy filesystem to be used for Git Alternates. + // If none is provided, it falls back to using the underlying instance used for + // DotGit. + AlternatesFS billy.Filesystem } // NewStorage returns a new Storage backed by a given `fs.Filesystem` and cache. @@ -49,6 +53,7 @@ func NewStorage(fs billy.Filesystem, cache cache.Object) *Storage { func NewStorageWithOptions(fs billy.Filesystem, cache cache.Object, ops Options) *Storage { dirOps := dotgit.Options{ ExclusiveAccess: ops.ExclusiveAccess, + AlternatesFS: ops.AlternatesFS, } dir := dotgit.NewWithOptions(fs, dirOps) diff --git a/vendor/github.com/go-git/go-git/v5/utils/ioutil/common.go b/vendor/github.com/go-git/go-git/v5/utils/ioutil/common.go index b0ace4e628f..235af717bcb 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/ioutil/common.go +++ b/vendor/github.com/go-git/go-git/v5/utils/ioutil/common.go @@ -195,7 +195,7 @@ func NewWriterOnError(w io.Writer, notify func(error)) io.Writer { } // NewWriteCloserOnError returns a io.WriteCloser that call the notify function -//when an unexpected (!io.EOF) error happens, after call Write function. +// when an unexpected (!io.EOF) error happens, after call Write function. func NewWriteCloserOnError(w io.WriteCloser, notify func(error)) io.WriteCloser { return NewWriteCloser(NewWriterOnError(w, notify), w) } @@ -208,13 +208,3 @@ func (r *writerOnError) Write(p []byte) (n int, err error) { return } - -type PipeReader interface { - io.ReadCloser - CloseWithError(err error) error -} - -type PipeWriter interface { - io.WriteCloser - CloseWithError(err error) error -} diff --git a/vendor/github.com/go-git/go-git/v5/utils/ioutil/pipe.go b/vendor/github.com/go-git/go-git/v5/utils/ioutil/pipe.go deleted file mode 100644 index f30c452fa4e..00000000000 --- a/vendor/github.com/go-git/go-git/v5/utils/ioutil/pipe.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !js - -package ioutil - -import "io" - -func Pipe() (PipeReader, PipeWriter) { - return io.Pipe() -} diff --git a/vendor/github.com/go-git/go-git/v5/utils/ioutil/pipe_js.go b/vendor/github.com/go-git/go-git/v5/utils/ioutil/pipe_js.go deleted file mode 100644 index cf102e6ef83..00000000000 --- a/vendor/github.com/go-git/go-git/v5/utils/ioutil/pipe_js.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build js - -package ioutil - -import "github.com/acomagu/bufpipe" - -func Pipe() (PipeReader, PipeWriter) { - return bufpipe.New(nil) -} diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go index ad169ff4aea..7bba0d03e31 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go +++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go @@ -103,6 +103,10 @@ func (n *node) calculateChildren() error { continue } + if file.Mode()&os.ModeSocket != 0 { + continue + } + c, err := n.newChildNode(file) if err != nil { return err diff --git a/vendor/github.com/go-git/go-git/v5/utils/trace/trace.go b/vendor/github.com/go-git/go-git/v5/utils/trace/trace.go new file mode 100644 index 00000000000..3e15c5b9f90 --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/utils/trace/trace.go @@ -0,0 +1,55 @@ +package trace + +import ( + "fmt" + "log" + "os" + "sync/atomic" +) + +var ( + // logger is the logger to use for tracing. + logger = newLogger() + + // current is the targets that are enabled for tracing. + current atomic.Int32 +) + +func newLogger() *log.Logger { + return log.New(os.Stderr, "", log.Ltime|log.Lmicroseconds|log.Lshortfile) +} + +// Target is a tracing target. +type Target int32 + +const ( + // General traces general operations. + General Target = 1 << iota + + // Packet traces git packets. + Packet +) + +// SetTarget sets the tracing targets. +func SetTarget(target Target) { + current.Store(int32(target)) +} + +// SetLogger sets the logger to use for tracing. +func SetLogger(l *log.Logger) { + logger = l +} + +// Print prints the given message only if the target is enabled. +func (t Target) Print(args ...interface{}) { + if int32(t)¤t.Load() != 0 { + logger.Output(2, fmt.Sprint(args...)) // nolint: errcheck + } +} + +// Printf prints the given message only if the target is enabled. +func (t Target) Printf(format string, args ...interface{}) { + if int32(t)¤t.Load() != 0 { + logger.Output(2, fmt.Sprintf(format, args...)) // nolint: errcheck + } +} diff --git a/vendor/github.com/go-git/go-git/v5/worktree.go b/vendor/github.com/go-git/go-git/v5/worktree.go index f8b854dda75..ad525c1a494 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree.go +++ b/vendor/github.com/go-git/go-git/v5/worktree.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "runtime" "strings" "github.com/go-git/go-billy/v5" @@ -95,7 +96,15 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { head, err := w.r.Head() if err == nil { - headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash()) + // if we don't have a shallows list, just ignore it + shallowList, _ := w.r.Storer.Shallow() + + var earliestShallow *plumbing.Hash + if len(shallowList) > 0 { + earliestShallow = &shallowList[0] + } + + headAheadOfRef, err := isFastForward(w.r.Storer, ref.Hash(), head.Hash(), earliestShallow) if err != nil { return err } @@ -104,7 +113,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error { return NoErrAlreadyUpToDate } - ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash()) + ff, err := isFastForward(w.r.Storer, head.Hash(), ref.Hash(), earliestShallow) if err != nil { return err } @@ -188,7 +197,12 @@ func (w *Worktree) Checkout(opts *CheckoutOptions) error { return w.Reset(ro) } + func (w *Worktree) createBranch(opts *CheckoutOptions) error { + if err := opts.Branch.Validate(); err != nil { + return err + } + _, err := w.r.Storer.Reference(opts.Branch) if err == nil { return fmt.Errorf("a branch named %q already exists", opts.Branch) @@ -381,6 +395,9 @@ func (w *Worktree) resetWorktree(t *object.Tree) error { b := newIndexBuilder(idx) for _, ch := range changes { + if err := w.validChange(ch); err != nil { + return err + } if err := w.checkoutChange(ch, t, b); err != nil { return err } @@ -390,6 +407,104 @@ func (w *Worktree) resetWorktree(t *object.Tree) error { return w.r.Storer.SetIndex(idx) } +// worktreeDeny is a list of paths that are not allowed +// to be used when resetting the worktree. +var worktreeDeny = map[string]struct{}{ + // .git + GitDirName: {}, + + // For other historical reasons, file names that do not conform to the 8.3 + // format (up to eight characters for the basename, three for the file + // extension, certain characters not allowed such as `+`, etc) are associated + // with a so-called "short name", at least on the `C:` drive by default. + // Which means that `git~1/` is a valid way to refer to `.git/`. + "git~1": {}, +} + +// validPath checks whether paths are valid. +// The rules around invalid paths could differ from upstream based on how +// filesystems are managed within go-git, but they are largely the same. +// +// For upstream rules: +// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/read-cache.c#L946 +// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/path.c#L1383 +func validPath(paths ...string) error { + for _, p := range paths { + parts := strings.FieldsFunc(p, func(r rune) bool { return (r == '\\' || r == '/') }) + if _, denied := worktreeDeny[strings.ToLower(parts[0])]; denied { + return fmt.Errorf("invalid path prefix: %q", p) + } + + if runtime.GOOS == "windows" { + // Volume names are not supported, in both formats: \\ and :. + if vol := filepath.VolumeName(p); vol != "" { + return fmt.Errorf("invalid path: %q", p) + } + + if !windowsValidPath(parts[0]) { + return fmt.Errorf("invalid path: %q", p) + } + } + + for _, part := range parts { + if part == ".." { + return fmt.Errorf("invalid path %q: cannot use '..'", p) + } + } + } + return nil +} + +// windowsPathReplacer defines the chars that need to be replaced +// as part of windowsValidPath. +var windowsPathReplacer *strings.Replacer + +func init() { + windowsPathReplacer = strings.NewReplacer(" ", "", ".", "") +} + +func windowsValidPath(part string) bool { + if len(part) > 3 && strings.EqualFold(part[:4], GitDirName) { + // For historical reasons, file names that end in spaces or periods are + // automatically trimmed. Therefore, `.git . . ./` is a valid way to refer + // to `.git/`. + if windowsPathReplacer.Replace(part[4:]) == "" { + return false + } + + // For yet other historical reasons, NTFS supports so-called "Alternate Data + // Streams", i.e. metadata associated with a given file, referred to via + // `::`. There exists a default stream + // type for directories, allowing `.git/` to be accessed via + // `.git::$INDEX_ALLOCATION/`. + // + // For performance reasons, _all_ Alternate Data Streams of `.git/` are + // forbidden, not just `::$INDEX_ALLOCATION`. + if len(part) > 4 && part[4:5] == ":" { + return false + } + } + return true +} + +func (w *Worktree) validChange(ch merkletrie.Change) error { + action, err := ch.Action() + if err != nil { + return nil + } + + switch action { + case merkletrie.Delete: + return validPath(ch.From.String()) + case merkletrie.Insert: + return validPath(ch.To.String()) + case merkletrie.Modify: + return validPath(ch.From.String(), ch.To.String()) + } + + return nil +} + func (w *Worktree) checkoutChange(ch merkletrie.Change, t *object.Tree, idx *indexBuilder) error { a, err := ch.Action() if err != nil { @@ -562,6 +677,11 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) { } func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { + // https://github.com/git/git/commit/10ecfa76491e4923988337b2e2243b05376b40de + if strings.EqualFold(f.Name, gitmodulesFile) { + return ErrGitModulesSymlink + } + from, err := f.Reader() if err != nil { return diff --git a/vendor/github.com/skeema/knownhosts/knownhosts.go b/vendor/github.com/skeema/knownhosts/knownhosts.go index c460031b48a..c2fb5160576 100644 --- a/vendor/github.com/skeema/knownhosts/knownhosts.go +++ b/vendor/github.com/skeema/knownhosts/knownhosts.go @@ -5,6 +5,7 @@ package knownhosts import ( "encoding/base64" "errors" + "fmt" "io" "net" "sort" @@ -68,8 +69,19 @@ func (hkcb HostKeyCallback) HostKeys(hostWithPort string) (keys []ssh.PublicKey) // known_hosts entries (for different key types), the result will be sorted by // known_hosts filename and line number. func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string) { - for _, key := range hkcb.HostKeys(hostWithPort) { - algos = append(algos, key.Type()) + // We ensure that algos never contains duplicates. This is done for robustness + // even though currently golang.org/x/crypto/ssh/knownhosts never exposes + // multiple keys of the same type. This way our behavior here is unaffected + // even if https://github.com/golang/go/issues/28870 is implemented, for + // example by https://github.com/golang/crypto/pull/254. + hostKeys := hkcb.HostKeys(hostWithPort) + seen := make(map[string]struct{}, len(hostKeys)) + for _, key := range hostKeys { + typ := key.Type() + if _, already := seen[typ]; !already { + algos = append(algos, typ) + seen[typ] = struct{}{} + } } return algos } @@ -140,11 +152,15 @@ func Line(addresses []string, key ssh.PublicKey) string { func WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.PublicKey) error { // Always include hostname; only also include remote if it isn't a zero value // and doesn't normalize to the same string as hostname. - addresses := []string{hostname} - remoteStr := remote.String() - remoteStrNormalized := Normalize(remoteStr) - if remoteStrNormalized != "[0.0.0.0]:0" && remoteStrNormalized != Normalize(hostname) { - addresses = append(addresses, remoteStr) + hostnameNormalized := Normalize(hostname) + if strings.ContainsAny(hostnameNormalized, "\t ") { + return fmt.Errorf("knownhosts: hostname '%s' contains spaces", hostnameNormalized) + } + addresses := []string{hostnameNormalized} + remoteStrNormalized := Normalize(remote.String()) + if remoteStrNormalized != "[0.0.0.0]:0" && remoteStrNormalized != hostnameNormalized && + !strings.ContainsAny(remoteStrNormalized, "\t ") { + addresses = append(addresses, remoteStrNormalized) } line := Line(addresses, key) + "\n" _, err := w.Write([]byte(line)) diff --git a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s index f3b653a12f3..6713accac09 100644 --- a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s +++ b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s @@ -199,8 +199,8 @@ TEXT ·mixBlocksSSE2(SB), 4, $0-32 MOVQ out+0(FP), DX MOVQ a+8(FP), AX MOVQ b+16(FP), BX - MOVQ a+24(FP), CX - MOVQ $128, BP + MOVQ c+24(FP), CX + MOVQ $128, DI loop: MOVOU 0(AX), X0 @@ -213,7 +213,7 @@ loop: ADDQ $16, BX ADDQ $16, CX ADDQ $16, DX - SUBQ $2, BP + SUBQ $2, DI JA loop RET @@ -222,8 +222,8 @@ TEXT ·xorBlocksSSE2(SB), 4, $0-32 MOVQ out+0(FP), DX MOVQ a+8(FP), AX MOVQ b+16(FP), BX - MOVQ a+24(FP), CX - MOVQ $128, BP + MOVQ c+24(FP), CX + MOVQ $128, DI loop: MOVOU 0(AX), X0 @@ -238,6 +238,6 @@ loop: ADDQ $16, BX ADDQ $16, CX ADDQ $16, DX - SUBQ $2, BP + SUBQ $2, DI JA loop RET diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go index 4f506f87912..199c21d27aa 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.7 && amd64 && gc && !purego +//go:build amd64 && gc && !purego package blake2b diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s index 353bb7cac5f..9ae8206c201 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.7 && amd64 && gc && !purego +//go:build amd64 && gc && !purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go deleted file mode 100644 index 1d0770abba4..00000000000 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.7 && amd64 && gc && !purego - -package blake2b - -import "golang.org/x/sys/cpu" - -func init() { - useSSE4 = cpu.X86.HasSSE41 -} - -//go:noescape -func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) - -func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { - if useSSE4 { - hashBlocksSSE4(h, c, flag, blocks) - } else { - hashBlocksGeneric(h, c, flag, blocks) - } -} diff --git a/vendor/golang.org/x/crypto/blake2b/register.go b/vendor/golang.org/x/crypto/blake2b/register.go index d9fcac3a4de..54e446e1d2c 100644 --- a/vendor/golang.org/x/crypto/blake2b/register.go +++ b/vendor/golang.org/x/crypto/blake2b/register.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.9 - package blake2b import ( diff --git a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s index 8fb26aebb2e..1f539388619 100644 --- a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s +++ b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s @@ -319,9 +319,9 @@ MOVQ rDi, _si(oState); \ MOVQ rDo, _so(oState) \ -// func keccakF1600(state *[25]uint64) +// func keccakF1600(a *[25]uint64) TEXT ·keccakF1600(SB), 0, $200-8 - MOVQ state+0(FP), rpState + MOVQ a+0(FP), rpState // Convert the user state into an internal state NOTQ _be(rpState) diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go index c0834c00dfe..cc0bb7ab648 100644 --- a/vendor/golang.org/x/crypto/ssh/channel.go +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -187,9 +187,11 @@ type channel struct { pending *buffer extPending *buffer - // windowMu protects myWindow, the flow-control window. - windowMu sync.Mutex - myWindow uint32 + // windowMu protects myWindow, the flow-control window, and myConsumed, + // the number of bytes consumed since we last increased myWindow + windowMu sync.Mutex + myWindow uint32 + myConsumed uint32 // writeMu serializes calls to mux.conn.writePacket() and // protects sentClose and packetPool. This mutex must be @@ -332,14 +334,24 @@ func (ch *channel) handleData(packet []byte) error { return nil } -func (c *channel) adjustWindow(n uint32) error { +func (c *channel) adjustWindow(adj uint32) error { c.windowMu.Lock() - // Since myWindow is managed on our side, and can never exceed - // the initial window setting, we don't worry about overflow. - c.myWindow += uint32(n) + // Since myConsumed and myWindow are managed on our side, and can never + // exceed the initial window setting, we don't worry about overflow. + c.myConsumed += adj + var sendAdj uint32 + if (channelWindowSize-c.myWindow > 3*c.maxIncomingPayload) || + (c.myWindow < channelWindowSize/2) { + sendAdj = c.myConsumed + c.myConsumed = 0 + c.myWindow += sendAdj + } c.windowMu.Unlock() + if sendAdj == 0 { + return nil + } return c.sendMessage(windowAdjustMsg{ - AdditionalBytes: uint32(n), + AdditionalBytes: sendAdj, }) } diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index bdc356cbdf1..fd8c49749eb 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -82,7 +82,7 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan if err := conn.clientHandshake(addr, &fullConf); err != nil { c.Close() - return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) + return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %w", err) } conn.mux = newMux(conn.transport) return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 49bbba76929..56cdc7c21c3 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -35,6 +35,16 @@ type keyingTransport interface { // direction will be effected if a msgNewKeys message is sent // or received. prepareKeyChange(*algorithms, *kexResult) error + + // setStrictMode sets the strict KEX mode, notably triggering + // sequence number resets on sending or receiving msgNewKeys. + // If the sequence number is already > 1 when setStrictMode + // is called, an error is returned. + setStrictMode() error + + // setInitialKEXDone indicates to the transport that the initial key exchange + // was completed + setInitialKEXDone() } // handshakeTransport implements rekeying on top of a keyingTransport @@ -100,6 +110,10 @@ type handshakeTransport struct { // The session ID or nil if first kex did not complete yet. sessionID []byte + + // strictMode indicates if the other side of the handshake indicated + // that we should be following the strict KEX protocol restrictions. + strictMode bool } type pendingKex struct { @@ -209,7 +223,10 @@ func (t *handshakeTransport) readLoop() { close(t.incoming) break } - if p[0] == msgIgnore || p[0] == msgDebug { + // If this is the first kex, and strict KEX mode is enabled, + // we don't ignore any messages, as they may be used to manipulate + // the packet sequence numbers. + if !(t.sessionID == nil && t.strictMode) && (p[0] == msgIgnore || p[0] == msgDebug) { continue } t.incoming <- p @@ -441,6 +458,11 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { return successPacket, nil } +const ( + kexStrictClient = "kex-strict-c-v00@openssh.com" + kexStrictServer = "kex-strict-s-v00@openssh.com" +) + // sendKexInit sends a key change message. func (t *handshakeTransport) sendKexInit() error { t.mu.Lock() @@ -454,7 +476,6 @@ func (t *handshakeTransport) sendKexInit() error { } msg := &kexInitMsg{ - KexAlgos: t.config.KeyExchanges, CiphersClientServer: t.config.Ciphers, CiphersServerClient: t.config.Ciphers, MACsClientServer: t.config.MACs, @@ -464,6 +485,13 @@ func (t *handshakeTransport) sendKexInit() error { } io.ReadFull(rand.Reader, msg.Cookie[:]) + // We mutate the KexAlgos slice, in order to add the kex-strict extension algorithm, + // and possibly to add the ext-info extension algorithm. Since the slice may be the + // user owned KeyExchanges, we create our own slice in order to avoid using user + // owned memory by mistake. + msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+2) // room for kex-strict and ext-info + msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) + isServer := len(t.hostKeys) > 0 if isServer { for _, k := range t.hostKeys { @@ -488,17 +516,24 @@ func (t *handshakeTransport) sendKexInit() error { msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat) } } + + if t.sessionID == nil { + msg.KexAlgos = append(msg.KexAlgos, kexStrictServer) + } } else { msg.ServerHostKeyAlgos = t.hostKeyAlgorithms // As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what // algorithms the server supports for public key authentication. See RFC // 8308, Section 2.1. + // + // We also send the strict KEX mode extension algorithm, in order to opt + // into the strict KEX mode. if firstKeyExchange := t.sessionID == nil; firstKeyExchange { - msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+1) - msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) msg.KexAlgos = append(msg.KexAlgos, "ext-info-c") + msg.KexAlgos = append(msg.KexAlgos, kexStrictClient) } + } packet := Marshal(msg) @@ -604,6 +639,13 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { return err } + if t.sessionID == nil && ((isClient && contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && contains(clientInit.KexAlgos, kexStrictClient))) { + t.strictMode = true + if err := t.conn.setStrictMode(); err != nil { + return err + } + } + // We don't send FirstKexFollows, but we handle receiving it. // // RFC 4253 section 7 defines the kex and the agreement method for @@ -679,6 +721,12 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { return unexpectedMessageError(msgNewKeys, packet[0]) } + if firstKeyExchange { + // Indicates to the transport that the first key exchange is completed + // after receiving SSH_MSG_NEWKEYS. + t.conn.setInitialKEXDone() + } + return nil } diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 7f0c236a9a2..c2dfe3268c5 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -213,6 +213,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha } else { for _, algo := range fullConf.PublicKeyAuthAlgorithms { if !contains(supportedPubKeyAuthAlgos, algo) { + c.Close() return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo) } } @@ -220,6 +221,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha // Check if the config contains any unsupported key exchanges for _, kex := range fullConf.KeyExchanges { if _, ok := serverForbiddenKexAlgos[kex]; ok { + c.Close() return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex) } } diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go index da015801ea5..0424d2d37c0 100644 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -49,6 +49,9 @@ type transport struct { rand io.Reader isClient bool io.Closer + + strictMode bool + initialKEXDone bool } // packetCipher represents a combination of SSH encryption/MAC @@ -74,6 +77,18 @@ type connectionState struct { pendingKeyChange chan packetCipher } +func (t *transport) setStrictMode() error { + if t.reader.seqNum != 1 { + return errors.New("ssh: sequence number != 1 when strict KEX mode requested") + } + t.strictMode = true + return nil +} + +func (t *transport) setInitialKEXDone() { + t.initialKEXDone = true +} + // prepareKeyChange sets up key material for a keychange. The key changes in // both directions are triggered by reading and writing a msgNewKey packet // respectively. @@ -112,11 +127,12 @@ func (t *transport) printPacket(p []byte, write bool) { // Read and decrypt next packet. func (t *transport) readPacket() (p []byte, err error) { for { - p, err = t.reader.readPacket(t.bufReader) + p, err = t.reader.readPacket(t.bufReader, t.strictMode) if err != nil { break } - if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { + // in strict mode we pass through DEBUG and IGNORE packets only during the initial KEX + if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) { break } } @@ -127,7 +143,7 @@ func (t *transport) readPacket() (p []byte, err error) { return p, err } -func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { +func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) { packet, err := s.packetCipher.readCipherPacket(s.seqNum, r) s.seqNum++ if err == nil && len(packet) == 0 { @@ -140,6 +156,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { select { case cipher := <-s.pendingKeyChange: s.packetCipher = cipher + if strictMode { + s.seqNum = 0 + } default: return nil, errors.New("ssh: got bogus newkeys message") } @@ -170,10 +189,10 @@ func (t *transport) writePacket(packet []byte) error { if debugTransport { t.printPacket(packet, true) } - return t.writer.writePacket(t.bufWriter, t.rand, packet) + return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode) } -func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { +func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error { changeKeys := len(packet) > 0 && packet[0] == msgNewKeys err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet) @@ -188,6 +207,9 @@ func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet [] select { case cipher := <-s.pendingKeyChange: s.packetCipher = cipher + if strictMode { + s.seqNum = 0 + } default: panic("ssh: no key material for msgNewKeys") } diff --git a/vendor/modules.txt b/vendor/modules.txt index 9c9d2f10cdf..00e81ce0e00 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -144,9 +144,6 @@ github.com/ProtonMail/go-crypto/openpgp/internal/ecc github.com/ProtonMail/go-crypto/openpgp/internal/encoding github.com/ProtonMail/go-crypto/openpgp/packet github.com/ProtonMail/go-crypto/openpgp/s2k -# github.com/acomagu/bufpipe v1.0.4 -## explicit; go 1.12 -github.com/acomagu/bufpipe # github.com/ahmetb/gen-crd-api-reference-docs v0.3.1-0.20220720053627-e327d0730470 => github.com/tektoncd/ahmetb-gen-crd-api-reference-docs v0.3.1-0.20220729140133-6ce2d5aafcb4 ## explicit; go 1.17 github.com/ahmetb/gen-crd-api-reference-docs @@ -315,7 +312,7 @@ github.com/cloudevents/sdk-go/v2/event/datacodec/xml github.com/cloudevents/sdk-go/v2/protocol github.com/cloudevents/sdk-go/v2/protocol/http github.com/cloudevents/sdk-go/v2/types -# github.com/cloudflare/circl v1.3.3 +# github.com/cloudflare/circl v1.3.7 ## explicit; go 1.19 github.com/cloudflare/circl/dh/x25519 github.com/cloudflare/circl/dh/x448 @@ -406,7 +403,7 @@ github.com/go-git/go-billy/v5/helper/polyfill github.com/go-git/go-billy/v5/memfs github.com/go-git/go-billy/v5/osfs github.com/go-git/go-billy/v5/util -# github.com/go-git/go-git/v5 v5.10.0 +# github.com/go-git/go-git/v5 v5.11.0 ## explicit; go 1.19 github.com/go-git/go-git/v5 github.com/go-git/go-git/v5/config @@ -453,6 +450,7 @@ github.com/go-git/go-git/v5/utils/merkletrie/index github.com/go-git/go-git/v5/utils/merkletrie/internal/frame github.com/go-git/go-git/v5/utils/merkletrie/noder github.com/go-git/go-git/v5/utils/sync +github.com/go-git/go-git/v5/utils/trace # github.com/go-jose/go-jose/v3 v3.0.1 ## explicit; go 1.12 github.com/go-jose/go-jose/v3 @@ -864,7 +862,7 @@ github.com/sigstore/sigstore/pkg/signature/kms/hashivault # github.com/sirupsen/logrus v1.9.3 ## explicit; go 1.13 github.com/sirupsen/logrus -# github.com/skeema/knownhosts v1.2.0 +# github.com/skeema/knownhosts v1.2.1 ## explicit; go 1.17 github.com/skeema/knownhosts # github.com/spf13/pflag v1.0.5 @@ -995,7 +993,7 @@ go.uber.org/zap/internal/stacktrace go.uber.org/zap/internal/ztest go.uber.org/zap/zapcore go.uber.org/zap/zaptest -# golang.org/x/crypto v0.16.0 +# golang.org/x/crypto v0.17.0 ## explicit; go 1.18 golang.org/x/crypto/argon2 golang.org/x/crypto/blake2b