Skip to content
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

Use native error-wrapping instead of pkg/errors #4

Closed
wants to merge 5 commits into from

Conversation

thaJeztah
Copy link

follow-up to #3, so opening as draft. Only the last two commits are new

This removes the dependency on pkg/errors, in favour of Go's native wrapping

  1. IsNotExist(): rewrite to use errors.Is(). This requires Go 1.13 or up
  2. Use native error-wrapping instead of pkg/errors

@thaJeztah thaJeztah mentioned this pull request Sep 30, 2020
@@ -28,23 +28,13 @@ var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join")
// accessed does not exist (or path components don't exist). This is
// effectively a more broad version of os.IsNotExist.
func IsNotExist(err error) bool {
// If it's a bone-fide ENOENT just bail.
if os.IsNotExist(errors.Cause(err)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure what kind of errors are supposed to be supplied as an argument here, but in case the error was obtained using errors.Wrap() from pkg/errors version > 0.9.0, the new code won't work as the Unwrap method only appeared in pkg/errors 0.9.0.

So, this either needs to be documented. Alternatively, we can do a runtime check, something like "if this is an error which has Cause method, it should also have Unwrap method, otherwise paniс saying the caller is using old pkg/errors". Complicated...

Copy link
Author

Choose a reason for hiding this comment

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

Ah, good one; I wanted to answer "but go mod enforces pkg/errors 0.9.1, but we remove that now of course 🤔

We can do a quick check which projects use this projects as a dependency (I think that list is limited, so we can at least make sure that they use a current pkg/errors)

Copy link
Author

Choose a reason for hiding this comment

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

Doing a quick scan using https://grep.app/search?q=github.com/cyphar/filepath-securejoin

Consumers (using pkg/errors v0.9.1))

Indirect

Indirect / potentially affected

Copy link
Contributor

Choose a reason for hiding this comment

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

https://github.com/fluxcd/flux (old pkg/errors, but using go 1.15)

Updated to 0.9.1

https://github.com/flynn/flynn (old pkg/errors)

No longer maintained.

https://github.com/bblfsh/bblfshd (indirect dependency, uses older pkg/errors)

Appears to be no longer maintained (last commits are about removing maintainers)

https://github.com/ContainerSolutions/helm-monitor (indirect dependency, uses older pkg/errors)

Appears to be no longer maintained (last commit is from 2019)

https://github.com/deepmind/kapitan (indirect dependency, uses older pkg/errors)
https://github.com/helm/monocular (indirect dependency, uses older pkg/errors)

These to appear to be alive but uses older pkg/errors; can be updated though.

So I guess we can proceed with the change.

join.go Outdated
)

// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been
// evaluated in attempting to securely join the two given paths.
var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join")
var ErrSymlinkLoop = fmt.Errorf("secure join: %w", syscall.ELOOP)
Copy link
Contributor

Choose a reason for hiding this comment

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

I would do this instead:

 // ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been
 // evaluated in attempting to securely join the two given paths.
-var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join")
+//
+// Deprecated: use errors.Is(err, syscall.ELOOP) instead.
+var ErrSymlinkLoop = syscall.ELOOP
 
 // IsNotExist tells you if err is an error that implies that either the path
 // accessed does not exist (or path components don't exist). This is
@@ -68,7 +59,7 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
        n := 0
        for unsafePath != "" {
                if n > 255 {
-                       return "", ErrSymlinkLoop
+                       return "", &os.PathError{Op: "SecureJoin", Path: root + "/" + unsafePath, Err: syscall.ELOOP}
                }
 
                // Next path component, p.

@kolyshkin
Copy link
Contributor

Ah, I just did a similar patch and then found this one :) which also needs some updating.

@thaJeztah do you think you can revive this?

@cyphar do you think it can be merged? It should be good to ditch pkg/errors

@cyphar
Copy link
Owner

cyphar commented Jun 12, 2021

Yeah I'd be happy to take something like this now (the lack of backtraces is sad, but ultimately error messages are usually enough in practice).

@kolyshkin
Copy link
Contributor

the lack of backtraces is sad

It was part of the proposal that ended up in adding %w, errors.Is and errors.As, but dropped later (golang/go#29934 (comment)). The "beta" version of stdlib
errors package, https://pkg.go.dev/golang.org/x/xerrors, still provides the backtraces.

Practically I saw a need for a backtrace once or twice, when the error itself is not good enough
(like when a bare unix errno was returned), so proper error wrapping (or adding context by using
e.g. os.PathError, os.SyscallError and the likes) should eliminate the need for backtraces.
The only problem here is without auditing all the source code (or having some linter to do so)
to make sure it does not return bare errors (such as ones from unix and syscall) we can't
be sure that we're good. Alas, I don't know about such a linter.

@cyphar
Copy link
Owner

cyphar commented Jun 13, 2021

Seems like something that errorlint should be updated to have. But in any case, switching to native wrapping is fine by me.

This patch replaces the vendor.conf with a go.mod, and re-vendors
the pkg/errors files from upstream.

This commit is mostly to preserve history of the local changes that
were made to the pkg/errors package (which appear to have been made
for debugging purposes).

Setting the minimum go version in go.mod to v1.7 for this commit,
which matches the lowest version that's testet against in CI.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
raising the minimum go version in go.mod 1.12, to fetch the
dependency module.

Updated travis to test against 1.12 (minimum version) and
1.15 (current stable).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This requires Go 1.13 or up

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
@thaJeztah
Copy link
Author

@kolyshkin rebased, and pushed a commit with your suggestion

Let me know if you want to do #3 first / separately, and/or if you want some commits to be squashed.

// If it's a bone-fide ENOENT just bail, also check if it's not actually
// an ENOTDIR, which in some cases is a more convoluted case of ENOENT
// (usually involving weird paths).
if errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would do

return os.IsNotExist(err) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)

(mostly because os.IsNotExist could do more than errors.Is(err, os.ErrNotExist).

Copy link
Contributor

Choose a reason for hiding this comment

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

I take it back; as @thaJeztah pointed out elsewhere, the official docs call for using errors.Is(err, os.ErrNotExist).

cyphar added a commit that referenced this pull request Jun 15, 2021
Kir Kolyshkin (3):
  Ditch pkg/errors, use native error (un)wrapping
  go.mod: add
  travis-ci: update Go versions

LGTM: cyphar
Closes #7 #4 #3
@cyphar
Copy link
Owner

cyphar commented Jun 15, 2021

Closing in favour of #7.

@cyphar cyphar closed this Jun 15, 2021
@thaJeztah thaJeztah deleted the native_wrapping branch June 15, 2021 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants