cisagov/RedEye

OperatorResolvers missing @Authorized decorator

LiveOverflow opened this issue · 1 comments

Describe the bug
The OperatorResolvers are missing @Authorized() decorators, which allows anybody to query operator names without authentication.

To Reproduce
Send this POST request to the /graphql endpoint of RedEye:

POST /api/graphql HTTP/1.1
Host: localhost:4000
content-type: application/json
Content-Length: 67

{"query":"query test { globalOperators { __typename id name } }"

}

The response will leak the registered operator names. Example response:

{"data":{"globalOperators":[{"__typename":"GlobalOperator","id":"dev","name":"dev"},{"__typename":"GlobalOperator","id":"analyst01","name":"analyst01"},{"__typename":"GlobalOperator","id":"john.doe","name":"john.doe"},{"__typename":"GlobalOperator","id":"dev2","name":"dev2"}]}}

The mutation also is lacking the @Authorized decorator, thus allows creating global operators without authentication:

{

  "query": "mutation createGlobalOperator($username: String!) { createGlobalOperator(username: $username) { id name } }",
  "variables": {
    "username": "john.doe"
  }
}

To fix, simply add the @Authorized decorator to all queries.

Background Info

We created this CodeQL query live on stream to find the resolvers without the authorized decorator:

import javascript

from Decorator d1
where 
    d1.getAChildExpr().(CallExpr).getCalleeName() 
            in ["Mutation", "Query"]
    and 
    not d1.getParent().getAChild().(Decorator).getAChildExpr().(CallExpr).getCalleeName() in ["Authorized"]
select d1, "is not authorized"

We only found these affected files, the progress resolver is uninteresting. And the issue with the operator resolver is mentioned above.

  • RedEye/applications/server/src/store/progress-resolvers.ts
  • RedEye/applications/server/src/store/operator-resolvers.ts

This is awesome; thank you for digging into this and writing such a detailed ticket!! The query’s name is a bit confusing on our part; the globalOperators query and mutation are only called on the login page. A single password protects the RedEye server, and populating the user field with operators from campaigns is a great way to match RedEye users with operators using the tool. We should rename that to globalUsers instead, as it’s just a list of usernames to log in with and pre-populated based on campaign data. We could make this secure and still be available for populating the login page!

I’ll need to create an alternate Authorized decorator, which checks if a password arg is correct. The current Authorized decorator checks for the cookie set after login.

I’ll look into this Monday and hopefully have a quick fix; thank you again for the excellent ticket!