-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Protect Package Bindings from containing circular references #4122
Protect Package Bindings from containing circular references #4122
Conversation
Codecov Report
@@ Coverage Diff @@
## master #4122 +/- ##
=========================================
- Coverage 86.48% 81.1% -5.39%
=========================================
Files 151 151
Lines 7272 7276 +4
Branches 468 467 -1
=========================================
- Hits 6289 5901 -388
- Misses 983 1375 +392
Continue to review full report at Codecov.
|
@@ -243,7 +243,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities { | |||
wp.binding map { | |||
// pre-existing entity is a binding, check that new binding is valid | |||
b => | |||
checkBinding(b.fullyQualifiedName) | |||
checkBinding(binding.fullyQualifiedName) |
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.
this was previously checking the existing binding, and not the incoming binding from content.binding
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.
indeed - this is a bug.
i would change the line to _ => checkBinding(binding.fullyQualifiedName)
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.
This also suggests that we need a test along these lines:
wsk package create p
wsk package bind p b
wsk package delete p
wsk package create p2
wsk package bind p2 b # will fail without your patch, and should succeed with
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.
the error message for fetching a binding missing its references package is confusing - we should open an issue to also fix that.
currently returns: The requested resource 'guest/b' does not exist.
instead of Binding references a package that does not exist.
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.
@rabbah I don't see how the last line (wsk package update p2 b
) will succeed currently. As b
is a binding already, and updating a binding to another binding was already not allowed
Packages.scala:L206
// trying to create a new package binding that refers to another binding
case provider if provider.binding.nonEmpty =>
Future.failed(RejectRequest(BadRequest, Messages.bindingCannotReferenceBinding))
While attempting to run your suggested test, I noticed that the command wsk update p2 b
isn't accepted, and I'm wondering whether you intended to use wsk bind p2 b
or another operation? Since p2
is created as a package it shouldn't allow it to be updated as a binding (from my understanding). My apologies if my inference is incorrect here.
$ wsk -i package create p
ok: created package p
$ wsk -i package bind p b
ok: created binding b
$ wsk -i package delete p
ok: deleted package p
$ wsk -i package create p2
ok: created package p2
$ wsk -i package update p2 b
error: Invalid argument(s): b. A package name is required.
Run 'wsk --help' for usage.
$ wsk -i package bind p2 b
error: Binding creation failed: resource already exists (code MjyhYzbZlRWsxjpmNToG4enhbK1j6EYz)
the error message for fetching a binding missing its references package is confusing - we should open an issue to also fix that.
Do you mean that after p
is deleted, attempting to fetch b
is failing with that error?
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.
@rabbah note that your example (with ?overwrite=true
) returns 400 as expected today as pointed out in the source.
PUT /api/v1/namespaces/guest/packages/b?overwrite=true HTTP/1.1
Content-Type: application/json
{"binding":{"namespace":"guest","name":"p2"}}
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"error":"Binding references a package that does not exist.","code":"HWkcauMms0QCAFNs4mhDCrtx7EbqHBWF"}
Do you still think there is another failure case we should resolve in this PR?
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.
the error message for fetching a binding missing its references package is confusing - we should open an issue to also fix that.
currently returns:
The requested resource 'guest/b' does not exist.
instead ofBinding references a package that does not exist.
I've created issue #4130 to track this
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.
@asteed the error "Binding references a package that does not exist" is wrong since p2
does exist. The controller tried to look up p
which was deleted and hence the reason the error is wrong. If the error contained the missing package, it would be:
> wsk package bind p2 b
error: Binding references a package 'p' that does not exist.
which is nonsensical since we're requesting to bind p2
and p
is no longer relevant.
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.
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.
i think your patch may fix/hide this bug.
i'm ok with a separate pr if the test can be written against the new code.
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.
Thanks for catching and fixing this.
}) | ||
if (docid == wp.docid) { | ||
logging.error(this, "unexpected package binding refers to itself: $docid") | ||
terminate(InternalServerError) |
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.
this should be a 40x bad request/user error not a server error; in any case, i don't see a test for this response (which may not be possible after your fix i think but we can unit test this method separately).
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.
Two points about this:
- I'm not quite sure I 100% agree with a
400 Bad Request
in this situation as there is nothing in fact wrong with the request, however there is entirely something wrong with the stored state of the server. While I also don't agree with a500 Internal Server Error
I don't see a better alternative for either. While it's use is intended for WebDAV, it almost appears that508 Loop Detected
would be the most accurate description :) - There is the attempt at performing the
get
on the object afterward, and that the test checks it is a 200 response, however you're correct that with the fix for ingest in place it would only be possible to manually craft this bad record (and I'm unable to determine how to do that given the current tests)
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.
@rabbah WDYT?
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.
We've avoided using some of the WebDAV codes (207 was another I used at one point and we removed although it was applicable for us in practice). I agree with you that if the data record is corrupted and now circular, it's not the client's fault but it's also not entirely out of their control (deleting the record should restore a proper invariant and the client can resume their operations).
If we keep the 500 error, we could at least provide a more meaningful error message.
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.
Seems like a 422 is pretty spot on "422 UNPROCESSABLE ENTITY"
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.
I can agree on 422 Unprocessable Entity
- I'll make those changes.
@@ -243,7 +243,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities { | |||
wp.binding map { | |||
// pre-existing entity is a binding, check that new binding is valid | |||
b => | |||
checkBinding(b.fullyQualifiedName) | |||
checkBinding(binding.fullyQualifiedName) |
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.
indeed - this is a bug.
i would change the line to _ => checkBinding(binding.fullyQualifiedName)
@@ -243,7 +243,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities { | |||
wp.binding map { | |||
// pre-existing entity is a binding, check that new binding is valid | |||
b => | |||
checkBinding(b.fullyQualifiedName) | |||
checkBinding(binding.fullyQualifiedName) |
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.
This also suggests that we need a test along these lines:
wsk package create p
wsk package bind p b
wsk package delete p
wsk package create p2
wsk package bind p2 b # will fail without your patch, and should succeed with
@@ -243,7 +243,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities { | |||
wp.binding map { | |||
// pre-existing entity is a binding, check that new binding is valid | |||
b => | |||
checkBinding(b.fullyQualifiedName) | |||
checkBinding(binding.fullyQualifiedName) |
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.
the error message for fetching a binding missing its references package is confusing - we should open an issue to also fix that.
currently returns: The requested resource 'guest/b' does not exist.
instead of Binding references a package that does not exist.
@@ -98,7 +98,10 @@ class PackageCollection(entityStore: EntityStore)(implicit logging: Logging) ext | |||
val pkgOwner = namespaces.contains(binding.namespace.asString) | |||
val pkgDocid = binding.docid | |||
logging.debug(this, s"checking subject has privilege '$right' for bound package '$pkgDocid'") | |||
checkPackageReadPermission(namespaces, pkgOwner, pkgDocid) | |||
if (doc == pkgDocid) |
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.
should this be treated as an error also?
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.
The way the flow works here, this is a precursor to the call into mergePackageWithBinding
(where we now will fail). My rationalization here was that this is merely a permissions check, and if we're checking the children of self, we've already checked self and that passed or we'd not be here.
I'm up to suggestions on whether this should fail, however. WDYT?
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.
I'm inclined to fail early since this too is a circular reference that indicates a corrupted or buggy record. Your point is valid though 🤔
c029c83
to
5db5977
Compare
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.
one small bug to fix otherwise LGTM.
core/controller/src/main/scala/org/apache/openwhisk/core/controller/Packages.scala
Outdated
Show resolved
Hide resolved
core/controller/src/main/scala/org/apache/openwhisk/core/entitlement/PackageCollection.scala
Outdated
Show resolved
Hide resolved
…who is bound to itself
5db5977
to
14e2011
Compare
6cf7aea
to
b40c898
Compare
@asteed Should we also update swagger definition for 422 status code response in package api? |
…4122) Use incoming package binding for update when calling checkBinding and add protection to prevent following circular package bindings.
Description
We ran across an issue where a package binding can contain a reference to itself. When this happens, any attempt at reading the package results in an infinite loop, eventually leading to the controller falling over. This can occur by simply performing the following commands using the
wsk
tool.There are two changes contained in this PR
Related issue and scope
My changes affect the following components
Types of changes
Checklist: