Compare commits

...

3 Commits

Author SHA1 Message Date
Alex Collins
89be1c9ce6 fixes tests 2019-12-10 14:38:52 -08:00
Alex Collins
d16f29f0ac Update manifests to v1.3.6 2019-12-10 14:08:42 -08:00
Simon Behar
5c1f581584 Add support for hidden directories with directory enforcer (#2821)
* Add support for hidden directories with directory enforcer

* Refactor

* Lint

* Rework done, still needs tests

* WIP

* Should be done

* Fix test

* Helm Charts
2019-12-10 14:07:40 -08:00
22 changed files with 156 additions and 99 deletions

View File

@@ -1 +1 @@
1.3.5
1.3.6

View File

@@ -784,7 +784,7 @@ func getLocalObjects(app *argoappv1.Application, local, appLabelKey, kubeVersion
}
func getLocalObjectsString(app *argoappv1.Application, local, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions) []string {
res, err := repository.GenerateManifests(local, &repoapiclient.ManifestRequest{
res, err := repository.GenerateManifests(local, "/", &repoapiclient.ManifestRequest{
ApplicationSource: &app.Spec.Source,
AppLabelKey: appLabelKey,
AppLabelValue: app.Name,

View File

@@ -12,7 +12,7 @@ bases:
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: v1.3.5
newTag: v1.3.6
- name: argoproj/argocd-ui
newName: argoproj/argocd-ui
newTag: v1.3.5
newTag: v1.3.6

View File

@@ -18,7 +18,7 @@ bases:
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: v1.3.5
newTag: v1.3.6
- name: argoproj/argocd-ui
newName: argoproj/argocd-ui
newTag: v1.3.5
newTag: v1.3.6

View File

@@ -2982,7 +2982,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -3036,7 +3036,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -3092,7 +3092,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -3166,7 +3166,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2897,7 +2897,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2951,7 +2951,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -3007,7 +3007,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -3081,7 +3081,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2746,7 +2746,7 @@ spec:
- "20"
- --operation-processors
- "10"
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2800,7 +2800,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2864,7 +2864,7 @@ spec:
- argocd-repo-server
- --redis
- argocd-redis:6379
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2915,7 +2915,7 @@ spec:
- argocd-server
- --staticassets
- /shared/app
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2661,7 +2661,7 @@ spec:
- "20"
- --operation-processors
- "10"
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2715,7 +2715,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2779,7 +2779,7 @@ spec:
- argocd-repo-server
- --redis
- argocd-redis:6379
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2830,7 +2830,7 @@ spec:
- argocd-server
- --staticassets
- /shared/app
image: argoproj/argocd:v1.3.5
image: argoproj/argocd:v1.3.6
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -117,7 +117,7 @@ func (s *Service) runRepoOperation(
repo *v1alpha1.Repository,
source *v1alpha1.ApplicationSource,
getCached func(revision string) bool,
operation func(appPath string, revision string) error,
operation func(appPath, repoRoot, revision string) error,
settings operationSettings) error {
var gitClient git.Client
@@ -158,7 +158,7 @@ func (s *Service) runRepoOperation(
return err
}
defer util.Close(closer)
return operation(chartPath, revision)
return operation(chartPath, chartPath, revision)
} else {
s.repoLock.Lock(gitClient.Root())
defer s.repoLock.Unlock(gitClient.Root())
@@ -174,7 +174,7 @@ func (s *Service) runRepoOperation(
if err != nil {
return err
}
return operation(appPath, revision)
return operation(appPath, gitClient.Root(), revision)
}
}
@@ -194,9 +194,9 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq
}
return false
}
err := s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, getCached, func(appPath string, revision string) error {
err := s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, getCached, func(appPath, repoRoot, revision string) error {
var err error
res, err = GenerateManifests(appPath, q)
res, err = GenerateManifests(appPath, repoRoot, q)
if err != nil {
return err
}
@@ -218,7 +218,7 @@ func getHelmRepos(repositories []*v1alpha1.Repository) []helm.HelmRepository {
return repos
}
func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
func helmTemplate(appPath string, repoRoot string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
templateOpts := &helm.TemplateOpts{
Name: q.AppLabelValue,
Namespace: q.Namespace,
@@ -236,22 +236,24 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
for _, val := range appHelm.ValueFiles {
// If val is not a URL, run it against the directory enforcer. If it is a URL, use it without checking
if _, err := url.ParseRequestURI(val); err != nil {
baseDirectoryPath, err := security.SubtractRelativeFromAbsolutePath(appPath, q.ApplicationSource.Path)
// Ensure that the repo root provided is absolute
absRepoPath, err := filepath.Abs(repoRoot)
if err != nil {
return nil, err
}
absBaseDir, err := filepath.Abs(baseDirectoryPath)
if err != nil {
return nil, err
}
if !filepath.IsAbs(val) {
// If the path to the file is relative, join it with the current working directory (appPath)
path := val
if !filepath.IsAbs(path) {
absWorkDir, err := filepath.Abs(appPath)
if err != nil {
return nil, err
}
val = filepath.Join(absWorkDir, val)
path = filepath.Join(absWorkDir, path)
}
_, err = security.EnforceToCurrentRoot(absBaseDir, val)
_, err = security.EnforceToCurrentRoot(absRepoPath, path)
if err != nil {
return nil, err
}
@@ -312,7 +314,7 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
}
// GenerateManifests generates manifests from a path
func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
func GenerateManifests(appPath, repoRoot string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
var targetObjs []*unstructured.Unstructured
var dest *v1alpha1.ApplicationDestination
@@ -325,7 +327,7 @@ func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient
case v1alpha1.ApplicationSourceTypeKsonnet:
targetObjs, dest, err = ksShow(q.AppLabelKey, appPath, q.ApplicationSource.Ksonnet)
case v1alpha1.ApplicationSourceTypeHelm:
targetObjs, err = helmTemplate(appPath, q)
targetObjs, err = helmTemplate(appPath, repoRoot, q)
case v1alpha1.ApplicationSourceTypeKustomize:
k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(), repoURL)
targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions)
@@ -618,7 +620,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
return false
}
err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, getCached, func(appPath string, revision string) error {
err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, getCached, func(appPath, repoRoot, revision string) error {
appSourceType, err := GetAppSourceType(q.Source, appPath)
if err != nil {
return err

View File

@@ -42,6 +42,7 @@ func newServiceWithMocks(root string) (*Service, *gitmocks.Client, *helmmocks.Cl
gitClient.On("CommitSHA").Return(mock.Anything, nil)
gitClient.On("Root").Return(root)
helmClient.On("CleanChartCache", mock.Anything, mock.Anything).Return(nil)
helmClient.On("ExtractChart", mock.Anything, mock.Anything).Return(func(chart string, version string) string {
return path.Join(root, chart)
}, util.NewCloser(func() error {
@@ -81,8 +82,8 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
assert.Equal(t, countOfManifests, len(res1.Manifests))
// this will test concatenated manifests to verify we split YAMLs correctly
res2, err := GenerateManifests("./testdata/concatenated", &q)
assert.Nil(t, err)
res2, err := GenerateManifests("./testdata/concatenated", "/", &q)
assert.NoError(t, err)
assert.Equal(t, 3, len(res2.Manifests))
})
}
@@ -210,7 +211,6 @@ func TestGenerateHelmWithValues(t *testing.T) {
// since the requested value is sill under the repo directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed
func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) {
service := newService("../..")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppLabelValue: "test",
@@ -223,6 +223,57 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) {
},
})
assert.NoError(t, err)
// Test the case where the path is "."
service = newService("./testdata/my-chart")
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppLabelValue: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
},
})
assert.NoError(t, err)
}
// This is a Helm first-class app with a values file inside the repo directory
// (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is allowed
func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) {
service := newService(".")
source := &argoappv1.ApplicationSource{
Chart: "./testdata/my-chart",
TargetRevision: "1.1.0",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"./my-chart-values.yaml"},
},
}
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
response, err := service.GenerateManifest(context.Background(), request)
assert.NoError(t, err)
assert.NotNil(t, response)
assert.Equal(t, &apiclient.ManifestResponse{
Manifests: []string{"{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"},
Namespace: "",
Server: "",
Revision: "1.1.0",
SourceType: "Helm",
}, response)
}
// This is a Helm first-class app with a values file outside the repo directory
// (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is not allowed
func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) {
service := newService(".")
source := &argoappv1.ApplicationSource{
Chart: "./testdata/my-chart",
TargetRevision: "1.0.0",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"../my-chart-2/my-chart-2-values.yaml"},
},
}
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
_, err := service.GenerateManifest(context.Background(), request)
assert.Error(t, err, "should be on or under current directory")
}
func TestGenerateHelmWithURL(t *testing.T) {
@@ -246,7 +297,6 @@ func TestGenerateHelmWithURL(t *testing.T) {
// (`~/go/src/github.com/argoproj/argo-cd`), so it is blocked
func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
service := newService("../..")
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppLabelValue: "test",
@@ -259,6 +309,20 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
},
})
assert.Error(t, err, "should be on or under current directory")
service = newService("./testdata/my-chart")
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: &argoappv1.Repository{},
AppLabelValue: "test",
ApplicationSource: &argoappv1.ApplicationSource{
Path: ".",
Helm: &argoappv1.ApplicationSourceHelm{
ValueFiles: []string{"../my-chart-2/values.yaml"},
Values: `cluster: {slaveCount: 2}`,
},
},
})
assert.Error(t, err, "should be on or under current directory")
}
func TestGenerateNullList(t *testing.T) {
@@ -342,7 +406,7 @@ func TestGenerateFromUTF16(t *testing.T) {
q := apiclient.ManifestRequest{
ApplicationSource: &argoappv1.ApplicationSource{},
}
res1, err := GenerateManifests("./testdata/utf-16", &q)
res1, err := GenerateManifests("./testdata/utf-16", "/", &q)
assert.Nil(t, err)
assert.Equal(t, 2, len(res1.Manifests))
}
@@ -359,6 +423,8 @@ func TestListApps(t *testing.T) {
"invalid-kustomize": "Kustomize",
"kustomization_yaml": "Kustomize",
"kustomization_yml": "Kustomize",
"my-chart": "Helm",
"my-chart-2": "Helm",
}
assert.Equal(t, expectedApps, res.Apps)
}

View File

@@ -0,0 +1,2 @@
name: my-chart
version: 1.1.0

View File

@@ -0,0 +1 @@
app: go

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: my-map

View File

@@ -0,0 +1,2 @@
name: my-chart
version: 1.1.0

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: my-map

View File

@@ -184,6 +184,20 @@ func TestKubeVersion(t *testing.T) {
})
}
func TestHelmValuesHiddenDirectory(t *testing.T) {
Given(t).
Path(".hidden-helm").
When().
AddFile("foo.yaml", "").
Create().
AppSet("--values", "foo.yaml").
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(HealthIs(HealthStatusHealthy)).
Expect(SyncStatusIs(SyncStatusCodeSynced))
}
func TestHelmWithDependencies(t *testing.T) {
testHelmWithDependencies(t, false)
}

View File

@@ -0,0 +1,2 @@
version: 1.0.0
name: helm

View File

@@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: my-map
data:
foo: bar

View File

View File

@@ -17,36 +17,19 @@ func EnforceToCurrentRoot(currentRoot, requestedPath string) (string, error) {
return requestedDir + string(filepath.Separator) + requestedFile, nil
}
func SubtractRelativeFromAbsolutePath(abs, rel string) (string, error) {
if len(rel) == 0 {
return abs, nil
}
if rel[0] == '.' {
return SubtractRelativeFromAbsolutePath(abs, rel[1:])
}
if rel[0] != '/' {
return SubtractRelativeFromAbsolutePath(abs, "/"+rel)
}
if rel[len(rel)-1] == '/' {
return SubtractRelativeFromAbsolutePath(abs, rel[:len(rel)-1])
}
rel = filepath.Clean(rel)
lastIndex := strings.LastIndex(abs, rel)
if lastIndex < 0 {
// This should be unreachable, because by this point the App Path will have already been validated by Path in
// util/app/path/path.go
return "", fmt.Errorf("app path is not under repo path (unreachable and most likely a bug)")
}
return abs[:lastIndex], nil
}
func isRequestedDirUnderCurrentRoot(currentRoot, requestedDir string) bool {
func isRequestedDirUnderCurrentRoot(currentRoot, requestedPath string) bool {
if currentRoot == string(filepath.Separator) {
return true
} else if currentRoot == requestedDir {
} else if currentRoot == requestedPath {
return true
}
return strings.HasPrefix(requestedDir, currentRoot+string(filepath.Separator))
if requestedPath[len(requestedPath)-1] != '/' {
requestedPath = requestedPath + "/"
}
if currentRoot[len(currentRoot)-1] != '/' {
currentRoot = currentRoot + "/"
}
return strings.HasPrefix(requestedPath, currentRoot)
}
func parsePath(path string) (string, string) {

View File

@@ -24,32 +24,3 @@ func TestEnforceToCurrentRoot(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "/home/argo/helmapp/values.yaml", cleanDir)
}
func TestSubtractRelativeFromAbsolutePath(t *testing.T) {
for _, test := range []string{"env", "/env", "env/", "/env/", "./env"} {
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
assert.NoError(t, err)
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env/guestbook", subtracted)
}
for _, test := range []string{"guestbook/env", "/guestbook/env", "guestbook/env/", "/guestbook/env/", "./guestbook/env"} {
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
assert.NoError(t, err)
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env", subtracted)
}
for _, test := range []string{"", ".", "/", "./"} {
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
assert.NoError(t, err)
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env/guestbook/env", subtracted)
}
// "Dirty" strings
for _, test := range []string{"guestbook/foo/../env", "/guestbook//env", "../guestbook/env/", "/../guestbook/env/", "./guestbook/env///"} {
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
assert.NoError(t, err)
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env", subtracted)
}
// Invalid strings
for _, test := range []string{"/not/in/path", "../not/in/path", "not/in/path"} {
_, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
assert.Error(t, err)
}
}