-
Notifications
You must be signed in to change notification settings - Fork 5
fix(artifact): confine local file loader to root dir with os.Root #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ import ( | |
|
|
||
| type FileLoader struct { | ||
| Root string | ||
| root *os.Root | ||
| } | ||
|
|
||
| // New validates cfg and constructs a FileLoader. It returns an error if | ||
|
|
@@ -25,7 +26,13 @@ func New(cfg Config) (FileLoader, error) { | |
| if err := cfg.Validate(); err != nil { | ||
| return FileLoader{}, err | ||
| } | ||
| return FileLoader(cfg), nil | ||
| // root confines every Load call to cfg.Root at the OS level, backing up | ||
| // the explicit escape check below. | ||
| root, err := os.OpenRoot(cfg.Root) | ||
| if err != nil { | ||
| return FileLoader{}, fmt.Errorf("local loader: open root %q: %w", cfg.Root, err) | ||
| } | ||
| return FileLoader{Root: cfg.Root, root: root}, nil | ||
| } | ||
|
|
||
| func (l FileLoader) Load(ctx context.Context, path string) ([]byte, error) { | ||
|
|
@@ -34,7 +41,7 @@ func (l FileLoader) Load(ctx context.Context, path string) ([]byte, error) { | |
| if err != nil || strings.HasPrefix(rel, "..") { | ||
| return nil, fmt.Errorf("%w: path %q escapes root %q", artifact.ErrNotFound, path, l.Root) | ||
| } | ||
| data, err := os.ReadFile(fullPath) | ||
| data, err := l.root.ReadFile(path) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository conventions and learnings ---'
find /tmp/coderabbit-repo-knowledge/opennsw-core-c6d4395b -maxdepth 2 -type f -name '*.md' -print
for f in /tmp/coderabbit-repo-knowledge/opennsw-core-c6d4395b/*/*.md; do
case "$f" in
*/*artifact*/*|*/*load*/*|*/*go*/*|*/*security*/*|*/*api*/*|*/*lifecycle*/*)
printf '\n--- %s ---\n' "$f"
cat "$f"
;;
esac
done
printf '%s\n' '--- changed file diff ---'
git diff -- artifact/loaders/local/local.go
printf '%s\n' '--- local loader source ---'
cat -n artifact/loaders/local/local.go
printf '%s\n' '--- module/toolchain declarations ---'
find . -maxdepth 3 \( -name go.mod -o -name go.work -o -name '*.yaml' -o -name '*.yml' \) -type f -print \
| sort
for f in $(find . -maxdepth 3 -name go.mod -type f -print); do
printf '\n--- %s ---\n' "$f"
sed -n '1,80p' "$f"
done
printf '%s\n' '--- FileLoader construction and lifecycle references ---'
rg -n -C 3 'FileLoader|New.*Loader|local\.|OpenRoot|ReadFile|\.Close\(' --glob '*.go' .Repository: OpenNSW/core Length of output: 50368 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository-wide conventions ---'
cat /tmp/coderabbit-repo-knowledge/opennsw-core-c6d4395b/learnings/repo-wide.md
printf '%s\n' '--- relevant loader definitions and tests ---'
cat -n artifact/loaders/local/config.go
cat -n artifact/loaders/factory.go | sed -n '60,95p'
cat -n artifact/loaders/local/local_test.go
printf '%s\n' '--- repository state for the reviewed file ---'
git status --short
git diff --stat
git diff --cached --stat
git show HEAD:artifact/loaders/local/local.go 2>/dev/null | sed -n '15,50p' || true
printf '%s\n' '--- exact direct FileLoader references ---'
rg -n -F 'local.FileLoader' --glob '*.go' .
rg -n -F 'FileLoader{' --glob '*.go' artifact
printf '%s\n' '--- Go toolchain configuration ---'
rg -n -i '(^|[^[:alnum:]])(go-version|toolchain|go 1\.)' \
go.mod go.work .github .golangci.yml Makefile 2>/dev/null || trueRepository: OpenNSW/core Length of output: 7163 🌐 Web query:
💡 Result: GO-2026-4970 is a security vulnerability (CVE-2026-39822) identified in the Go programming language's os.Root functionality on Unix systems [1][2][3]. The flaw allowed for a root escape: when a path provided to an os.Root operation ended in a trailing slash and the final path component was a symbolic link, the system would incorrectly follow that symlink even if it pointed to a location outside the defined root directory [1][4][5]. This issue was addressed in the Go 1.25.12 and 1.26.5 releases [6][5]. The fix involves sanitizing the path parameters to properly handle trailing slashes and ensuring that symbolic link resolution behavior is restricted to stay within the intended root [4][7][5]. The os.Root API, which was introduced in Go 1.24, is the only component affected; programs that do not utilize this API remain unaffected [8]. Citations:
Use a patched Go toolchain for the confinement guarantee. The module declares 🤖 Prompt for AI Agents |
||
| if err != nil { | ||
| if errors.Is(err, fs.ErrNotExist) { | ||
| return nil, fmt.Errorf("%w: local file not found at %s", artifact.ErrNotFound, fullPath) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🔎 Supported by static analysis
🏁 Script executed:
Repository: OpenNSW/core
Length of output: 15665
🏁 Script executed:
Repository: OpenNSW/core
Length of output: 5140
🏁 Script executed:
Repository: OpenNSW/core
Length of output: 31192
Preserve the public
FileLoaderconstruction contract.FileLoaderpreviously had the same underlying structure asConfig, andNewusedFileLoader(cfg). The added unexportedrootfield breaks that conversion for existing callers. A direct or zero-valueFileLoaderalso reachesl.root.ReadFilewith a nilrootand can panic.If external callers use
FileLoader, preserve the existing construction path or make the breaking change explicit throughNew. Add a downstream compile test.🤖 Prompt for AI Agents