Skip to content

Commit

Permalink
Ci 1502 (#184)
Browse files Browse the repository at this point in the history
* Update dependencies

* Add new input

* Add file redacting

* Add secret sharing

* Add e2e test

* Add failure warning to the input

* Simplify writer handling

* Simpler mock exclusion

* Remove env var expansion

* Skip redaction when possible

* Update fileredactor/fileredactor.go

Co-authored-by: lpusok <[email protected]>

* Update errors

---------

Co-authored-by: lpusok <[email protected]>
  • Loading branch information
tothszabi and lpusok authored Sep 14, 2023
1 parent 1a13f7f commit a699eb8
Show file tree
Hide file tree
Showing 33 changed files with 1,637 additions and 76 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ This metadata will be saved with the individual files and restored by the [Pull
| `addon_api_token` | The token required to authenticate with the API. | sensitive | `$ADDON_VDTESTING_API_TOKEN` |
| `public_install_page_url_map_format` | Provide a language template description using [Golang templates](https://golang.org/pkg/text/template) so that the **Deploy to Bitrise.io** Step can build the required custom output. | required | `{{range $index, $element := .}}{{if $index}}\|{{end}}{{$element.File}}=>{{$element.URL}}{{end}}` |
| `permanent_download_url_map_format` | Provide a language template description using [Golang templates](https://golang.org/pkg/text/template) so that the **Deploy to Bitrise.io** Step can build the required custom output for the permanent download URL. | required | `{{range $index, $element := .}}{{if $index}}\|{{end}}{{$element.File}}=>{{$element.URL}}{{end}}` |
| `files_to_redact` | A newline (`\n`) separated list of file paths to redact secrets from before the step deploys them. | | |
| `debug_mode` | The Step will print more verbose logs if enabled. | required | `false` |
</details>

Expand Down
49 changes: 48 additions & 1 deletion bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ app:
workflows:
test:
steps:
- go-list: {}
- go-list:
inputs:
- exclude: |-
*/vendor/*
*/mocks
- golint: {}
- errcheck: {}
- go-test: {}
Expand All @@ -28,6 +32,7 @@ workflows:
- _download_sample_artifacts
- test
after_run:
- _file_redaction
- _zero_length_file_upload
- _test_results_deployment
steps:
Expand Down Expand Up @@ -246,6 +251,48 @@ workflows:
- deploy_path: 0_length_file.txt
- debug_mode: "true"

_file_redaction:
steps:
- script:
is_always_run: true
inputs:
- content: |-
#!/bin/bash
set -ex
# Create empty temporary file
temp_file_path=$(mktemp)
# Add some content
echo "this is not a secret\nthis is a secret\nagain not a secret\n" > $temp_file_path
# Export the file and the secret as env vars
envman add --key FILE_TO_REDACT --value "${temp_file_path}"
envman add --key SUPER_SECRET --value "this is a secret" --sensitive
- path::./:
title: File redaction test
run_if: true
inputs:
- build_url: $BITRISE_BUILD_URL
- build_api_token: $BITRISE_BUILD_API_TOKEN
- deploy_path: ""
- files_to_redact: $FILE_TO_REDACT
- debug_mode: "true"
- script:
is_always_run: true
inputs:
- content: |-
#!/bin/bash
set -ex
actual_content=$(<$FILE_TO_REDACT)
expected_content="this is not a secret\n[REDACTED]\nagain not a secret\n"
# Fail if the content of the redacted file does not meat our expected value
if [[ "$actual_content" != "$expected_content" ]]; then
echo "$FILE_TO_REDACT was not redacted"
echo "Actual content: "$actual_content""
echo "Expected content: "$expected_content""
exit 1
fi
generate_readme:
steps:
- git::https://github.com/bitrise-steplib/steps-readme-generator.git@main:
Expand Down
64 changes: 64 additions & 0 deletions fileredactor/filepathprocessor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package fileredactor

import (
"fmt"
"strings"

"github.com/bitrise-io/go-utils/v2/pathutil"
)

// FilePathProcessor is an interface for an entity which accepts file paths separated by the
// newline (`\n`) character and returns a slice of absolute paths.
type FilePathProcessor interface {
ProcessFilePaths(string) ([]string, error)
}

type filePathProcessor struct {
pathModifier pathutil.PathModifier
pathChecker pathutil.PathChecker
}

// NewFilePathProcessor returns a structure which implements the FilePathProcessor interface.
// The implementation includes handling filepaths defined as environment variables, relative file paths,
// and absolute file paths.
// The implementation also includes making sure the filepath exists and is not a directory.
func NewFilePathProcessor(modifier pathutil.PathModifier, checker pathutil.PathChecker) FilePathProcessor {
return filePathProcessor{
pathModifier: modifier,
pathChecker: checker,
}
}

func (f filePathProcessor) ProcessFilePaths(filePaths string) ([]string, error) {
filePaths = strings.TrimSpace(filePaths)
if filePaths == "" {
return nil, nil
}

var processedFilePaths []string

list := strings.Split(filePaths, "\n")
for _, item := range list {
item = strings.TrimSpace(item)
if item == "" {
continue
}

path, err := f.pathModifier.AbsPath(item)
if err != nil {
return nil, err
}

isDir, err := f.pathChecker.IsDirExists(path)
if err != nil {
return nil, fmt.Errorf("failed to check if path (%s) is a directory: %w", path, err)
}
if isDir {
return nil, fmt.Errorf("path (%s) is a directory and cannot be redacted, please make sure to only provide filepaths as inputs", path)
}

processedFilePaths = append(processedFilePaths, path)
}

return processedFilePaths, nil
}
67 changes: 67 additions & 0 deletions fileredactor/filepathprocessor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package fileredactor

import (
"reflect"
"testing"

"github.com/bitrise-steplib/steps-deploy-to-bitrise-io/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func Test_ProcessFilePaths(t *testing.T) {
deployDirPath := "/some/absolute/path/deploy_dir"
tests := []struct {
name string
input string
output []string
outputErr string
envs map[string]string
}{
{
name: "Empty input",
input: " ",
output: nil,
outputErr: "",
envs: nil,
},
{
name: "Expand relative path",
input: `
/some/absolute/path/to/file.txt
file_in_deploy_dir.txt
`,
output: []string{
"/some/absolute/path/to/file.txt",
"/some/absolute/path/deploy_dir/file_in_deploy_dir.txt",
},
outputErr: "",
envs: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockModifier := new(mocks.PathModifier)
mockModifier.On("AbsPath", "a_file.txt").Return(deployDirPath+"/a_file.txt", nil)
mockModifier.On("AbsPath", "/some/absolute/path/to/file.txt").Return("/some/absolute/path/to/file.txt", nil)
mockModifier.On("AbsPath", "file_in_deploy_dir.txt").Return(deployDirPath+"/file_in_deploy_dir.txt", nil)

mockChecker := new(mocks.PathChecker)
mockChecker.On("IsDirExists", mock.Anything).Return(false, nil)

pathProcessor := NewFilePathProcessor(mockModifier, mockChecker)
result, err := pathProcessor.ProcessFilePaths(tt.input)

if err != nil && tt.outputErr != "" {
assert.EqualError(t, err, tt.outputErr)
} else if err != nil {
t.Errorf("%s got = %v, want %v", t.Name(), err, tt.outputErr)
}

if !reflect.DeepEqual(result, tt.output) {
t.Errorf("%s got = %v, want %v", t.Name(), result, tt.output)
}
})
}
}
81 changes: 81 additions & 0 deletions fileredactor/fileredactor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package fileredactor

import (
"fmt"
"io"
"os"

"github.com/bitrise-io/go-utils/v2/fileutil"
"github.com/bitrise-io/go-utils/v2/log"
"github.com/bitrise-io/go-utils/v2/redactwriter"
)

const bufferSize = 64 * 1024

// FileRedactor is an interface for a structure which, given a slice of file paths and another slice of secrets can
// process the specified files to redact secrets from them.
type FileRedactor interface {
RedactFiles([]string, []string) error
}

type fileRedactor struct {
fileManager fileutil.FileManager
}

// NewFileRedactor returns a structure that implements the FileRedactor interface
func NewFileRedactor(manager fileutil.FileManager) FileRedactor {
return fileRedactor{
fileManager: manager,
}
}

func (f fileRedactor) RedactFiles(filePaths []string, secrets []string) error {
logger := log.NewLogger()
for _, path := range filePaths {
if err := f.redactFile(path, secrets, logger); err != nil {
return fmt.Errorf("failed to redact file (%s): %w", path, err)
}
}

return nil
}

func (f fileRedactor) redactFile(path string, secrets []string, logger log.Logger) error {
source, err := f.fileManager.Open(path)
if err != nil {
return fmt.Errorf("failed to open file for redaction (%s): %w", path, err)
}
defer func() {
if err := source.Close(); err != nil {
logger.Warnf("Failed to close file: %s", err)
}
}()

newPath := path + ".redacted"
destination, err := os.Create(newPath)
if err != nil {
return fmt.Errorf("failed to create temporary file for redaction: %w", err)
}
defer func() {
if err := destination.Close(); err != nil {
logger.Warnf("Failed to close file: %s", err)
}
}()

redactWriter := redactwriter.New(secrets, destination, logger)
if _, err := io.Copy(redactWriter, source); err != nil {
return fmt.Errorf("failed to redact secrets: %w", err)
}

if err := redactWriter.Close(); err != nil {
return fmt.Errorf("failed to close redact writer: %w", err)
}

//rename new file to old file name
err = os.Rename(newPath, path)
if err != nil {
return fmt.Errorf("failed to overwrite old file (%s) with redacted file: %w", path, err)
}

return nil
}
37 changes: 37 additions & 0 deletions fileredactor/fileredactor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package fileredactor

import (
"os"
"path"
"testing"

"github.com/bitrise-io/go-utils/v2/fileutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_RedactFiles(t *testing.T) {
secrets := []string{
"SUPER_SECRET_WORD",
"ANOTHER_SECRET_WORD",
}
filePath := path.Join(t.TempDir(), "step-output.txt")
content, err := os.ReadFile("testdata/before_redaction.txt")
require.NoError(t, err)

fileManager := fileutil.NewFileManager()
err = fileManager.WriteBytes(filePath, content)
require.NoError(t, err)

fileRedactor := NewFileRedactor(fileutil.NewFileManager())
err = fileRedactor.RedactFiles([]string{filePath}, secrets)
require.NoError(t, err)

got, err := os.ReadFile(filePath)
require.NoError(t, err)

want, err := os.ReadFile("testdata/after_redaction.txt")
require.NoError(t, err)

assert.Equal(t, want, got)
}
8 changes: 8 additions & 0 deletions fileredactor/testdata/after_redaction.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[REDACTED]

Normal output from any of our steps.
More output.

[REDACTED]

The end.
8 changes: 8 additions & 0 deletions fileredactor/testdata/before_redaction.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SUPER_SECRET_WORD

Normal output from any of our steps.
More output.

ANOTHER_SECRET_WORD

The end.
11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ go 1.20

require (
github.com/avast/apkparser v0.0.0-20210301101811-6256c76f738e
github.com/bitrise-io/bitrise v0.0.0-20210519130014-380842fb41c1
github.com/bitrise-io/envman v0.0.0-20210517135508-b2b4fe89eac5
github.com/bitrise-io/bitrise v0.0.0-20220808135808-3483087dd853
github.com/bitrise-io/envman v0.0.0-20220401145857-d11e00a5dc55
github.com/bitrise-io/go-android v0.0.0-20210517091621-72f0eb7a5197
github.com/bitrise-io/go-steputils v1.0.5
github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.20
github.com/bitrise-io/go-utils v1.0.9
github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.15
github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.19
github.com/bitrise-io/go-xcode v1.0.16
github.com/google/go-cmp v0.5.9
github.com/gorilla/mux v1.8.0
Expand All @@ -20,8 +21,8 @@ require (

require (
github.com/bitrise-io/go-pkcs12 v0.0.0-20230815095624-feb898696e02 // indirect
github.com/bitrise-io/goinp v0.0.0-20210504152833-8559b0680ab1 // indirect
github.com/bitrise-io/stepman v0.0.0-20210517135458-203f7a48d37a // indirect
github.com/bitrise-io/goinp v0.0.0-20211005113137-305e91b481f4 // indirect
github.com/bitrise-io/stepman v0.0.0-20220808095634-6e12d2726f30 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
Expand Down
Loading

0 comments on commit a699eb8

Please sign in to comment.