Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bindings/localstorage/localstorage.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ type LocalStorage struct {
// Metadata defines the metadata.
type Metadata struct {
RootPath string `json:"rootPath"`
FromTemp bool `json:"fromTemp"`
Comment on lines 59 to +60
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetComponentMetadata relies on metadata.GetMetadataInfoFromStructType, which uses mapstructure tags (and otherwise falls back to exported field names). Since these fields only have json tags, the metadata map will expose keys like RootPath/FromTemp rather than rootPath/fromTemp, diverging from metadata.yaml and other components. Consider adding mapstructure:"rootPath" and mapstructure:"fromTemp" tags (keeping the existing json tags if needed) so metadata introspection and decoding are consistent.

Suggested change
RootPath string `json:"rootPath"`
FromTemp bool `json:"fromTemp"`
RootPath string `json:"rootPath" mapstructure:"rootPath"`
FromTemp bool `json:"fromTemp" mapstructure:"fromTemp"`

Copilot uses AI. Check for mistakes.
}

type createResponse struct {
Expand Down Expand Up @@ -91,6 +92,10 @@ func (ls *LocalStorage) parseMetadata(meta bindings.Metadata) (*Metadata, error)
return nil, err
}

if m.FromTemp {
m.RootPath = filepath.Join(os.TempDir(), m.RootPath)
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When fromTemp is enabled, filepath.Join(os.TempDir(), m.RootPath) can be bypassed: an absolute rootPath will cause Join to ignore os.TempDir(), and values with .. can escape the temp directory. Also, an empty rootPath becomes the temp directory itself, bypassing the existing "rootPath must not be empty" validation. Consider rejecting empty/absolute paths when fromTemp is true and using securejoin.SecureJoin(os.TempDir(), m.RootPath) (or equivalent) to guarantee the resolved path stays under the temp dir before calling validateRootPath.

Suggested change
m.RootPath = filepath.Join(os.TempDir(), m.RootPath)
if strings.TrimSpace(m.RootPath) == "" {
return nil, errors.New("rootPath must not be empty")
}
if filepath.IsAbs(m.RootPath) {
return nil, errors.New("rootPath must not be absolute when fromTemp is enabled")
}
m.RootPath, err = securejoin.SecureJoin(os.TempDir(), m.RootPath)
if err != nil {
return nil, fmt.Errorf("invalid rootPath for temp directory: %w", err)
}

Copilot uses AI. Check for mistakes.
}

m.RootPath, err = validateRootPath(m.RootPath)
if err != nil {
return nil, err
Expand Down
15 changes: 14 additions & 1 deletion bindings/localstorage/localstorage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,21 @@ func TestParseMetadata(t *testing.T) {
meta, err := localStorage.parseMetadata(m)
require.NoError(t, err)
assert.Equal(t, path, meta.RootPath)
}

m.Properties = map[string]string{
"rootPath": "myapp_data",
"fromTemp": "true",
}
metaTemp, err := localStorage.parseMetadata(m)
require.NoError(t, err)

realTempDir, err := filepath.EvalSymlinks(os.TempDir())
require.NoError(t, err)
expectedTempPath := filepath.Join(realTempDir, "myapp_data")

assert.Equal(t, expectedTempPath, metaTemp.RootPath)
assert.True(t, metaTemp.FromTemp)
}
Comment on lines +41 to +54
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestParseMetadata covers the happy path for fromTemp=true, but doesn't exercise edge cases introduced by the new flag (for example: missing/empty rootPath, absolute rootPath, or rootPath containing ..). Adding assertions for these cases would help prevent regressions and verify that fromTemp cannot resolve outside the temp directory.

Copilot uses AI. Check for mistakes.
func TestValidateRootPath(t *testing.T) {
// Get the current working directory
cwd, err := os.Getwd()
Expand Down
5 changes: 5 additions & 0 deletions bindings/localstorage/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ metadata:
required: true
description: "The file name to write"
example: "data.txt"
- name: fromTemp
description: "If true, resolves rootPath from the system-wide temporary directory."
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description says "system-wide temporary directory", but os.TempDir() returns the process default temp directory (often influenced by env vars and frequently user-specific on Windows). Consider rewording to "system temporary directory"/"default temporary directory (os.TempDir())" to avoid overpromising behavior.

Suggested change
description: "If true, resolves rootPath from the system-wide temporary directory."
description: "If true, resolves rootPath from the default temporary directory (os.TempDir())."

Copilot uses AI. Check for mistakes.
type: bool
required: false
default: "false"