Skip to content

Commit

Permalink
refactor: attempt to make CopyFile more secure
Browse files Browse the repository at this point in the history
Redo the order-of-operations so that the destination file is nominally
non-readable, and does not attempt to create the file until all
info about the source file is in-hand.

Also change the default permissions on the `giltDir` and `cacheDir`
directories to owner-only.

Fixes: Issue #149
  • Loading branch information
nisimond authored and retr0h committed Feb 21, 2024
1 parent e0939ee commit a30f9b3
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 7 deletions.
2 changes: 1 addition & 1 deletion internal/repositories/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (r *Repositories) getCacheDir() (string, error) {

cacheDir := filepath.Join(giltDir, "cache")
if _, err := r.appFs.Stat(cacheDir); os.IsNotExist(err) {
if err := r.appFs.Mkdir(cacheDir, 0o755); err != nil {
if err := r.appFs.Mkdir(cacheDir, 0o700); err != nil {
return "", err
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/repositories/repositories_public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (suite *RepositoriesPublicTestSuite) TestOverlayDstDirExists() {

func (suite *RepositoriesPublicTestSuite) TestOverlayErrorRemovingDstDir() {
suite.mockRepo.EXPECT().Clone(gomock.Any(), gomock.Any()).Return("", nil)
_ = suite.appFs.MkdirAll(filepath.Join(suite.giltDir, "cache"), 0o755)
_ = suite.appFs.MkdirAll(filepath.Join(suite.giltDir, "cache"), 0o700)
_ = suite.appFs.MkdirAll(suite.dstDir, 0o755)
// Replace the test FS with a read-only copy
suite.appFs = afero.NewReadOnlyFs(suite.appFs)
Expand Down
15 changes: 12 additions & 3 deletions internal/repository/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,33 +58,42 @@ func (r *Copy) CopyFile(
slog.String("dstFile", dst),
)

// Open the source file for reading, and record its metadata
in, err := r.appFs.Open(src)
if err != nil {
return err
}
defer func() { _ = in.Close() }()

si, err := r.appFs.Stat(src)
if err != nil {
return err
}

// Open dest file for writing; make it owner-only perms before putting
// anything in it
out, err := r.appFs.Create(dst)
if err != nil {
return err
}
defer func() { _ = out.Close() }()

_, err = io.Copy(out, in)
err = r.appFs.Chmod(dst, 0o600)
if err != nil {
return err
}

err = out.Sync()
_, err = io.Copy(out, in)
if err != nil {
return err
}

si, err := r.appFs.Stat(src)
err = out.Sync()
if err != nil {
return err
}

// All done; make the permissions match
err = r.appFs.Chmod(dst, si.Mode())
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/repositories/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (r *Repositories) getGiltDir() (string, error) {
return "", err
}

if err := r.appFs.MkdirAll(dir, 0o755); err != nil {
if err := r.appFs.MkdirAll(dir, 0o700); err != nil {
return "", err
}

Expand Down
2 changes: 1 addition & 1 deletion test/integration/test_cli.bats
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ setup() {
GILT_CLONED_REPO_1_DST_DIR=${GILT_TEST_BASE_TMP_DIR}/retr0h.ansible-etcd
GILT_CLONED_REPO_2_DST_DIR=${GILT_TEST_BASE_TMP_DIR}/retr0h.ansible-etcd-tag

mkdir -p ${GILT_DIR}
mkdir -p -m 700 ${GILT_DIR}
mkdir -p ${GILT_LIBRARY_DIR}
mkdir -p ${GILT_ROLES_DIR}
cp test/Giltfile.yaml ${GILT_TEST_BASE_TMP_DIR}/Giltfile.yaml
Expand Down

0 comments on commit a30f9b3

Please sign in to comment.