Skip to content

Commit

Permalink
[extension/filestorage] replace path-unsafe characters in component n…
Browse files Browse the repository at this point in the history
…ames

Fixes open-telemetry#3148
  • Loading branch information
andrzej-stencel committed Jun 28, 2023
1 parent 5d7b22d commit b916f7a
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
16 changes: 16 additions & 0 deletions .chloggen/replace-slashes-in-component-names.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: breaking

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: extension/filestorage

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Replace path-unsafe characters in component names

# One or more tracking issues related to the change
issues: [3148]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
18 changes: 17 additions & 1 deletion extension/storage/filestorage/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"
"path/filepath"
"regexp"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/extension"
Expand Down Expand Up @@ -49,7 +50,7 @@ func (lfs *localFileStorage) GetClient(_ context.Context, kind component.Kind, e
} else {
rawName = fmt.Sprintf("%s_%s_%s_%s", kindString(kind), ent.Type(), ent.Name(), name)
}
// TODO sanitize rawName
rawName = sanitize(rawName)
absoluteName := filepath.Join(lfs.cfg.Directory, rawName)
client, err := newClient(lfs.logger, absoluteName, lfs.cfg.Timeout, lfs.cfg.Compaction)

Expand Down Expand Up @@ -84,3 +85,18 @@ func kindString(k component.Kind) string {
return "other" // not expected
}
}

// sanitize replaces characters in name that are not safe in a file path
func sanitize(name string) string {
// Specify unsafe characters by a negation of allowed characters:
// - uppercase and lowercase letters A-Z, a-z
// - digits 0-9
// - dot `.`
// - hyphen `-`
// - underscore `_`
// - tilde `~`
unsafeCharactersRegex := regexp.MustCompile(`[^A-Za-z0-9.\-_~]`)

// Replace all characters other than the above with the tilde `~`
return unsafeCharactersRegex.ReplaceAllString(name, "~")
}
52 changes: 52 additions & 0 deletions extension/storage/filestorage/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sync"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/extension/experimental/storage"
Expand Down Expand Up @@ -185,6 +186,57 @@ func TestTwoClientsWithDifferentNames(t *testing.T) {
require.Equal(t, myBytes2, data)
}

func TestSanitize(t *testing.T) {
testCases := []struct {
name string
componentName string
sanitizedName string
}{
{
name: "safe characters",
componentName: `.UPPERCASE-lowercase_1234567890~`,
sanitizedName: `.UPPERCASE-lowercase_1234567890~`,
},
{
name: "unsafe characters",
componentName: `slash/backslash\colon:asterisk*questionmark?quote'doublequote"angle<>pipe|exclamationmark!percent%space `,
sanitizedName: `slash~backslash~colon~asterisk~questionmark~quote~doublequote~angle~~pipe~exclamationmark~percent~space~`,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
assert.Equal(t, testCase.sanitizedName, sanitize(testCase.componentName))
})
}
}

func TestComponentNameWithUnsafeCharacters(t *testing.T) {
ctx := context.Background()

tempDir := t.TempDir()

f := NewFactory()
cfg := f.CreateDefaultConfig().(*Config)
cfg.Directory = tempDir

extension, err := f.CreateExtension(context.Background(), extensiontest.NewNopCreateSettings(), cfg)
require.NoError(t, err)

se, ok := extension.(storage.Extension)
require.True(t, ok)

client, err := se.GetClient(
ctx,
component.KindReceiver,
newTestEntity("my/slashed/omponent*"),
"",
)

require.NoError(t, err)
require.NotNil(t, client)
}

func TestGetClientErrorsOnDeletedDirectory(t *testing.T) {
ctx := context.Background()

Expand Down

0 comments on commit b916f7a

Please sign in to comment.