Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Add queries for CORS checking, Cookie attribute checking and unsafe signature generation #307

Closed
wants to merge 7 commits into from

Conversation

onkyoworm
Copy link

hello, i write some security rules of go in ql,
1、CORSChecking
2、CookieAttributesChecking
3、UnSafetySignatureGenerate

Copy link
Contributor

@max-schaefer max-schaefer left a comment

Choose a reason for hiding this comment

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

Many thanks for your contribution!

Could I ask that you move your queries into the ql/src/experimental folder (and tests into ql/test/experimental)? We generally prefer for new queries to be put there first. They can then be improved step by step, and ultimately may be moved into the default query suite. See https://github.com/github/codeql-go/blob/main/ql/docs/experimental.md for details.

That guide also provides more details about the expected formatting, style, and other aspects. I have pointed out a few places where your contribution needs a bit more work for one of the queries below, similar comments apply to the other queries.

Again, thank you for spending time on putting this pull request together; we look forward to improving it together with you.

/**
* @name CORS checking
* @description This rule is used to check your code whether config correctly about CORS(Cross-Origin Resource Sharing)
* @kind cors
Copy link
Contributor

Choose a reason for hiding this comment

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

(
(
c = f.getACall()
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"")
Copy link
Contributor

Choose a reason for hiding this comment

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

We generally discourage the use of toString() in queries (cf https://github.com/github/codeql-go/blob/main/ql/docs/experimental.md). In this case, I think you want

Suggested change
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"")
and c.getArgument(0).getStringValue() = "Access-Control-Allow-Origin"

(
c = f.getACall()
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"")
and c.getArgument(1).toString().matches("\"*\"")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
and c.getArgument(1).toString().matches("\"*\"")
and c.getArgument(1).getStringValue() = "*"

c = f.getACall()
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"")
and c.getArgument(1).toString().matches("\"*\"")
and s = "Critical! You set all origin can access, it could be attakced by CORS"
Copy link
Contributor

Choose a reason for hiding this comment

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

This alert message doesn't match our usual style. I would suggest you try and rephrase it in a more neutral tone, perhaps something like

Suggested change
and s = "Critical! You set all origin can access, it could be attakced by CORS"
and s = "Allowing access from arbitrary origins may facilitate CORS attacks."

Comment on lines 19 to 21
// f.getACall().getArgument(0).toString().matches("\"Access-Control-Allow-Credentials\"")
// and f.getACall().getArgument(1).toString().matches("\"true\"")
// and c = f.getACall()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// f.getACall().getArgument(0).toString().matches("\"Access-Control-Allow-Credentials\"")
// and f.getACall().getArgument(1).toString().matches("\"true\"")
// and c = f.getACall()

We don't normally check in commented-out code.

c = f.getACall()
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Credentials\"")
and c.getArgument(1).toString().matches("\"true\"")
and s = "Warning! You set send credentials as ture. If your config Access-Control-Allow-Origin unproperly, it could be attakced by CORS"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
and s = "Warning! You set send credentials as ture. If your config Access-Control-Allow-Origin unproperly, it could be attakced by CORS"
and s = "Allowing access to credentials can facilitate CORS attacks if Access-Control-Allow-Origin is configured improperly."

and c.getArgument(0).toString().matches("\"Access-Control-Allow-Credentials\"")
and c.getArgument(1).toString().matches("\"true\"")
and s = "Warning! You set send credentials as ture. If your config Access-Control-Allow-Origin unproperly, it could be attakced by CORS"
//c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
//c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"")

Comment on lines 32 to 33
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Methods\"")
and c.getArgument(1).toString().regexpMatch("(?i).*?(put|head|delete|options).*?")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Methods\"")
and c.getArgument(1).toString().regexpMatch("(?i).*?(put|head|delete|options).*?")
and c.getArgument(0).getStringValue() = "Access-Control-Allow-Methods" and
and c.getArgument(1).getStringValue().regexpMatch("(?i).*?(put|head|delete|options).*?")

c = f.getACall()
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Methods\"")
and c.getArgument(1).toString().regexpMatch("(?i).*?(put|head|delete|options).*?")
and s = "Info! Unexpected http method used, please check it"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
and s = "Info! Unexpected http method used, please check it"
and s = "Allowing HTTP methods such as PUT and DELETE may be dangerous."

)

)
select "Usage here: ", c, c.asExpr().getLocation(), s
Copy link
Contributor

Choose a reason for hiding this comment

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

Alert queries should select two things: a program element, and an alert message.

Suggested change
select "Usage here: ", c, c.asExpr().getLocation(), s
select c, s

@onkyoworm
Copy link
Author

thanks for your help !, i just fix it and commit again

Copy link
Contributor

@max-schaefer max-schaefer left a comment

Choose a reason for hiding this comment

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

Thanks! A few more comments and suggestions. Also, could you please auto-format your .ql files, either in VSCode or by running make autoformat from the command line?

}else {
message_1 := a + b
message_2 := []byte(message_1)
hashfucker := sha256.New()
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you rename this variable, please?

Copy link
Author

Choose a reason for hiding this comment

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

oh sorry i forgot to change it

@@ -0,0 +1,23 @@
<!DOCTYPE qhelp PUBLIC
Copy link
Contributor

Choose a reason for hiding this comment

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

It's fine for experimental queries not to have a .qhelp file, so if you don't quite know yet how to best explain what your query does, you can simply remove the .qhelp.

import go
from Function f, StructLit s, int x, string infor, string tmp
where
f.getName() = "SetCookie"
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like you're not actually using this variable for anything.

Copy link
Author

Choose a reason for hiding this comment

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

which one? do you mean x? infor?or tmp?
here is an explanation:
x : i found there is so many attributes in cookie, but you can't locate the "httponly" attributes by an extractly index,so i use a loop in [1 .. 20] to get the attributes call "httponly"
infor : this is easy, the information that i want to give to users
tmp: this is a liite complex to explain, i use "concat" to combine all of the attributes into a string, so i could check if the developer set "httponly" attributes or not

Copy link
Contributor

Choose a reason for hiding this comment

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

The variable f, which seems to be the only variable mentioned on this line. You are not using it for anything, so you might as well drop it.

Copy link
Contributor

Choose a reason for hiding this comment

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

x : i found there is so many attributes in cookie, but you can't locate the "httponly" attributes by an extractly index,so i use a loop in [1 .. 20] to get the attributes call "httponly"

There are no loops in CodeQL. When you say s.getKey(x) below, then that is enough to assign a value to x; you don't need to constrain it further, so you can just drop the x in [1..20] bit. (As mentioned below, you'll then also want to restrict the scope of x to the first part of the or by using an exists.)

from Function f, StructLit s, int x, string infor, string tmp
where
f.getName() = "SetCookie"
and s.getType().toString() = "Cookie"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
and s.getType().toString() = "Cookie"
and s.getType().hasQualifiedName("net/http", "Cookie")

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what this query does.

Copy link
Author

Choose a reason for hiding this comment

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

this rule is about the developer use a wrong way to check the signature that come from the user or web end
try to image a sceen like this,
url like this /check.php?para1=abc&para2=def&sign=xxxxxx
para1 and para2 is parameter that come from and can controled by users, and sign is the signature of them like sha1(para1+para2) or something others like this
but if the backend(server) ensure the signature easily just by calculate them like sha1(para1+) and compare it with the signature from web, this would be dangerous.

this would be show easily and dangerous in some payment scene,here is an example
you are buying some from ebay, such as 10 books, and each one is 10 dollers, the total count of them is 100 dollers
usually, web end needs send count and each books money to back end, and also a sign of them
just like /buy.php?count=10&perMoney=10&sign=xxxxx here sign = sha1(count+perMoney)
if the server calculate it by sha1(count+perMoney) as the web do, it could be bypass easily
because count + perMoney = 1010, if the hacker interrupt the request(burp or something others), and change count into '1' and perMoney into '010',no matter the web or ther server would get the same result as count '10' and perMoney into '10'
so the server would beileve that the paramater is correct and use it as usuall, this would do harm to the money or some logic of them

Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds interesting. Do you have a reference for this type of attack?

Copy link
Author

Choose a reason for hiding this comment

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

yeah, we have some real case of this type of attack (but in chinese),
but i don't the exactly name of this type of attack in english,
so i am spending times on finding the same of this type of attack in english

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

@smowton can't agree any more

and
call2.getCalleeName() = "Write"
and
source.asExpr() = f1.getACall().asExpr()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just source = f1.getACall()?

Copy link
Author

Choose a reason for hiding this comment

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

haha, i am jsut new to here, not so familiar with it, so sometimes write some rules that could work but so ugly
thanks for your suggestions, i will update it quickly,

and
call1 = f2.getACall()
and
call1.toString().matches(a1.getRhs().toString())
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
call1.toString().matches(a1.getRhs().toString())
a1.getRhs() = call1.asExpr()

and
call1.toString().matches(a1.getRhs().toString())
and
call2.getReceiver().toString() = a1.getLhs().toString()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
call2.getReceiver().toString() = a1.getLhs().toString()
exists(Variable x |
a1.getLhs() = x.getAReference() and
call1.getReceiver() = x.getARead()
)

However, I think it would be even better to express this using data flow. Then you don't need to reason about a1 at all: DataFlow::localFlow(call1, call2.getReceiver()).

a1.getRhs() = call1.asExpr()
and
(
b1.getLeftOperand().asExpr().toString() = a1.getLhs().toString() or
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to above: use Variable or data flow.

@smowton smowton changed the title add security rules Add querys for CORS checking, Cookie attribute checking and unsafe signature generation Aug 26, 2020
@smowton smowton changed the title Add querys for CORS checking, Cookie attribute checking and unsafe signature generation Add queries for CORS checking, Cookie attribute checking and unsafe signature generation Aug 26, 2020
c = f.getACall()
and c.getArgument(0).getStringValue() = "Access-Control-Allow-Credentials"
and c.getArgument(1).getStringValue() = "true"
and s = "Allowing access to credentials can facilitate CORS attacks if Access-Control-Allow-Origin is configured improperly."
Copy link
Contributor

Choose a reason for hiding this comment

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

Message doesn't match header

Copy link
Author

Choose a reason for hiding this comment

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

what about this
Allowing Access-Control-Allow-Credentials as true would expand you attack surface if CORS attacks appears

and
(
(
c = f.getACall()
Copy link
Contributor

Choose a reason for hiding this comment

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

You can pull this out rather than restate it in each or

c = f.getACall()
and c.getArgument(0).getStringValue() = "Access-Control-Allow-Methods"
and c.getArgument(1).getStringValue().regexpMatch("(?i).*?(put|head|delete|options).*?")
and s = "Allowing HTTP methods such as PUT and DELETE may be dangerous."
Copy link
Contributor

Choose a reason for hiding this comment

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

Allowing cross-origin HTTP methods

(
c = f.getACall()
and c.getArgument(0).getStringValue() = "Access-Control-Allow-Origin"
and c.getArgument(1).getStringValue() = "*"
Copy link
Contributor

Choose a reason for hiding this comment

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

Not necessary for this PR, but something to try out in the future: using the dataflow framework means we can pick up cases where either the header-name or the "*" isn't directly specified as a string constant, but gets there indirectly, through variables or function parameters / return values.

and
(
s.getKey(x).toString() = "HttpOnly"
and s.getAnElement().getChild(1).toString() = "false"
Copy link
Contributor

Choose a reason for hiding this comment

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

You could also use getAnElement().(KeyValueExpr) and then examine its getKey() and getValue() methods, to avoid having to explicitly specify the integer x at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a better idea than what I suggested above.

override predicate isSource(DataFlow::Node source) {
exists(Function f1, Function f2, DataFlow::CallNode call1, DataFlow::CallNode call2, Assignment a1 |
(
f1.hasQualifiedName("github.com/astaxie/beego.Controller", "GetString") or
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you probably want Method, a subclass of Function, and its three-argument hasQualifiedName function

f1.hasQualifiedName("github.com/astaxie/beego.Controller", "GetBool")
)
and
f2.hasQualifiedName("crypto/sha256", "New")
Copy link
Contributor

Choose a reason for hiding this comment

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

This concept applies just as well for different hash algorithms

Copy link
Author

Choose a reason for hiding this comment

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

yes, but in most code, i found they use sha256. Later could expand the range of the hash algorithms

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed; no need to do the generalisation for the initial experimental query.

(
f1.hasQualifiedName("github.com/astaxie/beego.Controller", "GetString") or
f1.hasQualifiedName("github.com/astaxie/beego.Controller", "GetStrings") or
f1.hasQualifiedName("github.com/astaxie/beego.Controller", "GetInt") or
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than specify these exact sources, you might want to use UntrustedFlowSource, a class representing generally data supplied by the user.

Copy link
Author

Choose a reason for hiding this comment

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

Good idea, but i have no idea that UntrustedFlowSource whether include this sources of framework "beego", so i just specify the sources by myself,
Later i would rewrite it with UntrustedFlowSource and test it

Copy link
Contributor

Choose a reason for hiding this comment

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

We do not have a built-in model for beego at the moment. Leaving this as-is and generalising later is probably fine.

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

@max-schaefer max-schaefer left a comment

Choose a reason for hiding this comment

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

Thank you for working with us on improving this query. I have a few more suggestions. Also, you will need to autoformat everything in order to pass our continuous integration tests.

Comment on lines 26 to 28
tmp = concat(int i| i in [0..s.getNumElement()]|s.getKey(i).(Ident).getName(), "|")
and not
tmp.matches("%HttpOnly%")
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for this level of trickery. You can simply do

Suggested change
tmp = concat(int i| i in [0..s.getNumElement()]|s.getKey(i).(Ident).getName(), "|")
and not
tmp.matches("%HttpOnly%")
not exists(KeyValueExpr k2 |
k2 = s.getAnElement() and
k2..getKey(i).(Ident).getName() = "HttpOnly"
)

and get rid of tmp.

Copy link
Author

Choose a reason for hiding this comment

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

awesome! i try many times using exists or any or for,but i could not fix this problem,so i have to use the tmp

Copy link
Author

Choose a reason for hiding this comment

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

to autoformat the ql file, using codeql query format file?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's right, yes.

Copy link
Contributor

Choose a reason for hiding this comment

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

(And don't forget to pass -i to make it update the file in place.)

Comment on lines 26 to 28
a1.getRhs() = call1.asExpr()
and
DataFlow::localFlow(call1, call2.getReceiver())
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that you are using localFlow you don't need a1 above any more.

and
DataFlow::localFlow(call1, call2.getReceiver())
and
call2.getCalleeName() = "Write"
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't look like call2 is used in the definition of source. Should it perhaps be used in some way?

a1.getRhs() = call1.asExpr()
and
(
DataFlow::localFlow(call1, b1.getLeftOperand())
Copy link
Contributor

Choose a reason for hiding this comment

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

As above, using localFlow means you don't need to reason about the assignment a1 any more, I think you can just remove it.

@@ -0,0 +1,53 @@
/*
* @name Unsafety Signature Generate checking
Copy link
Contributor

Choose a reason for hiding this comment

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

Slightly awkward name; perhaps change to something like

Suggested change
* @name Unsafety Signature Generate checking
* @name Unsafe signature calculation

Copy link
Contributor

@max-schaefer max-schaefer left a comment

Choose a reason for hiding this comment

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

Some more questions about the third query; also, the formatting still doesn't seem to be right.

m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetFloat") or
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetBool")
) and
c1.getCalleeName() = "Write" and
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the role of c1? It does not seem to have anything to do with source.

Copy link
Author

Choose a reason for hiding this comment

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

Interesting! i deleted c1 and found it work as usual
At first, i use c1 to ensure the code did something to calc the hash, like
hashCalc := sha256.New()
hashCalc.Write(message_2)
In Go, you must declare it, and then use it's Write method

but by just deleting the line23 , i found it work as usual,
thanks

Copy link
Contributor

Choose a reason for hiding this comment

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

So are you planning on deleting this line?

Copy link
Author

Choose a reason for hiding this comment

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

i am not sure it's suitable or not to delete this line, because during my test (with code i have been saw and i thought out), it still works if i delete it.
i have no idea it would work well or not if you delete it in others situation,.
but in seriously, i think it should not be deleted if you want it run in critial

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 you mean. This line is almost useless. The only thing it does is to ensure that there is a call to Write somewhere in the program. However, that call is not required to be related to source in any way at all. That doesn't sound like a very useful thing to check.

) and
c1.getCalleeName() = "Write" and
DataFlow::localFlow(m.getACall(), d) and
source = d.getASuccessor()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why getASuccessor()?

Copy link
Author

Choose a reason for hiding this comment

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

yeah, this is something different between using d and d.getASuccessor()
here is the result of using d
you would found there is two kind of result call to xxx and the varialbe using Getxxx of beego to get user input
In this way, you would found the hole life circle of the variable, but i think this is a lttile complex
because in some situation like having so many filter function with the user's input
you would find so many result with the query
and what i want is to focus the first entrance of it , and i found getASuccessor() work, so i use it
and the result is like the second photo
(if necessary, i could change it into just using d)
image

image

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 entirely sure I understand, but are you saying that you think there will be too many sources if you omit the getASuccessor? Have you observed this to actually be a problem?

I think it might be easiest to just define source = m.getACall() and omit d entirely. The global data-flow will follow any steps that localFlow follows anyway.

Copy link
Author

Choose a reason for hiding this comment

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

sounds great, let me try

m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetFloat") or
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetBool")
) and
c1.getCalleeName() = "Write" and
Copy link
Contributor

Choose a reason for hiding this comment

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

So are you planning on deleting this line?

) and
c1.getCalleeName() = "Write" and
DataFlow::localFlow(m.getACall(), d) and
source = d.getASuccessor()
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 entirely sure I understand, but are you saying that you think there will be too many sources if you omit the getASuccessor? Have you observed this to actually be a problem?

I think it might be easiest to just define source = m.getACall() and omit d entirely. The global data-flow will follow any steps that localFlow follows anyway.

c.getArgument(0).getStringValue() = "Access-Control-Allow-Credentials" and
c.getArgument(1).getStringValue() = "true" and
s =
"Allowing Access-Control-Allow-Credentials as true would expand you attack surface if CORS attacks appears。"
Copy link
Contributor

@max-schaefer max-schaefer Sep 18, 2020

Choose a reason for hiding this comment

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

Suggested change
"Allowing Access-Control-Allow-Credentials as true would expand you attack surface if CORS attacks appears。"
"Setting Access-Control-Allow-Credentials to true expands the attack surface for CORS attacks."

Copy link
Contributor

Choose a reason for hiding this comment

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

The period at the end of the message still needs to be fixed. I have also suggested a rewrite of the text to make the grammar a bit smoother.

chohanliang added 2 commits September 30, 2020 22:54
1. where get user input
2. where developer calcuate the hash with inputed paramater
3.  where they compare the web's hash result and developer 's
but if just using one source -> sink path, i think it hard to explain this situation(unsafe sign gen)
so i have to change the code into another form
(if any good idea about it, please tell me)
Copy link
Contributor

@max-schaefer max-schaefer left a comment

Choose a reason for hiding this comment

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

A few more suggestions. I also noticed that you haven't checked in the .qlref and .expected files for your tests, so at the moment no tests are being run.

c.getArgument(0).getStringValue() = "Access-Control-Allow-Credentials" and
c.getArgument(1).getStringValue() = "true" and
s =
"Allowing Access-Control-Allow-Credentials as true would expand you attack surface if CORS attacks appears。"
Copy link
Contributor

Choose a reason for hiding this comment

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

The period at the end of the message still needs to be fixed. I have also suggested a rewrite of the text to make the grammar a bit smoother.

@@ -0,0 +1,23 @@
import go
Copy link
Contributor

Choose a reason for hiding this comment

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

This query lacks metadata (@name, @description, etc.).

m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetFloat") or
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetBool")
) and
b.getOperator() = "==" and
Copy link
Contributor

Choose a reason for hiding this comment

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

You could also change the type of b to DataFlow::EqualityTestNode.

Comment on lines 19 to 20
b.getAnOperand().toString() = a1.getAnLhs().toString() and
b.getAnOperand().toString() = a.getAnLhs().toString()
Copy link
Contributor

Choose a reason for hiding this comment

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

As before, I think it would be nicer to phrase this in terms of data flow:

DataFlow::localFlow(m.getACall(), b.getAnOperand()) and
DataFlow::localFlow(c1, b.getAnOperand())

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I would suggest storing m.getACall() in a variable c so you can reference it in the select clause (see below).

b.getAnOperand().toString() = a1.getAnLhs().toString() and
b.getAnOperand().toString() = a.getAnLhs().toString()
)
select "Get input here: ", m.getACall(), "Get hash algorithm result here: ", c1,
Copy link
Contributor

Choose a reason for hiding this comment

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

This select doesn't have the right shape for an alert query. You can rewrite it like this:

select b, "Comparison between $@ and $@.", c, "user input", c1, "hash result"

Note that I am assuming here that you have a variable c that is bound to the value of m.getACall() used above. It is very important to introduce a new variable for this. If you use m.getACall() twice, then the two uses may refer to different calls to m.

d, _ := c.GetInt("para3")
e := c.GetString("sign")
f := c.GetString("sign2")
if a == "" && b = "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if a == "" && b = "" {
if a == "" && b == "" {

2. add metadata of UnSafeSignatureGenerate.ql
3. use DataFlow::EqualityTestNode instead
4. use a nicer parser
Copy link
Contributor

@max-schaefer max-schaefer left a comment

Choose a reason for hiding this comment

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

One more thing I just noticed: none of your new queries have an @id. Could you add them? Also, were you planning on adding/enabling tests?

@@ -11,13 +18,11 @@ where
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetFloat") or
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetBool")
) and
c = m.getACall() and
b.getOperator() = "==" and
Copy link
Contributor

Choose a reason for hiding this comment

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

This check is no longer needed now that b has type DataFlow::EqualityTestNode.

c.getArgument(0).getStringValue() = "Access-Control-Allow-Credentials" and
c.getArgument(1).getStringValue() = "true" and
s =
"If \"Access-Control-Allow-Credentials\" is set as \"TURE\", you are easier to be attacked when CORS attacks happen."
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"If \"Access-Control-Allow-Credentials\" is set as \"TURE\", you are easier to be attacked when CORS attacks happen."
"Setting \"Access-Control-Allow-Credentials\" to \"true\" may facilitate CORS attacks."

Copy link
Author

Choose a reason for hiding this comment

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

Thanks schaefer, i have fixed what you suggestion, but i still have i question: i am using mac, but i found it always failed in Step: Test Linux (Ubuntu) when i commit
Should i build an Ubuntu enviroment and test the code in it before i commit?
For example, test -z "$(git ls-files | grep '\.go$' | grep -v ^vendor/ | xargs grep -L "//\s*autoformat-ignore" | xargs gofmt -l)" this commang show no errors in mac, but in codeql test, it shows errors

Copy link
Contributor

Choose a reason for hiding this comment

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

There are a few platform-independent checks, which are run as part of the "Test Linux (Ubuntu)" step, but not as part of the other steps. This test failure in particular means that you need to autoformat your .go files; you can use make autoformat to do that.

Copy link
Author

Choose a reason for hiding this comment

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

thanks, i would reformat the .go file
Besides, what do you mean adding/enabling tests? i have added the demo code with go in ql/test/experimental do i need to add another testing?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, you need to add a .qlref file referring to the query next to it. See here for an example of how that is done. Then you use codeql test run to run the tests as explained here.

Copy link
Author

Choose a reason for hiding this comment

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

here told me when 0 tests passed; 1 tests failed: appear means i should add a .expected file. And i found some expected file here but after reading it, i am confuse about how to get it?
here is my command result

x@x codeql-go % ~/code-audit/codeql-cli/codeql/codeql test run --search-path=ql ql/test/experimental/UnsafetySignatureGenerate
Executing 1 tests in 1 directories.
Extracting test database in /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate.
Upgrading database for /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate.
Could not upgrade the dataset in /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate: Unrecoverable mismatch between extractor and library dbschemes.
Extractor produced: (bcbec1b0e44ae4365dd4e5bade5aec80135a4a00) /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.testproj/db-go/go.dbscheme
We can upgrade to: (bcbec1b0e44ae4365dd4e5bade5aec80135a4a00) /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.testproj/db-go/go.dbscheme
The query expects: (4affa49dbe2bbab1a33f0e3ea6b045116abbcfda) /Users/choohan/forgit/mygit/codeql-go/ql/src/go.dbscheme
Unrecoverable mismatch between extractor and library dbschemes.
Extractor produced: (bcbec1b0e44ae4365dd4e5bade5aec80135a4a00) /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.testproj/db-go/go.dbscheme
We can upgrade to: (bcbec1b0e44ae4365dd4e5bade5aec80135a4a00) /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.testproj/db-go/go.dbscheme
The query expects: (4affa49dbe2bbab1a33f0e3ea6b045116abbcfda) /Users/choohan/forgit/mygit/codeql-go/ql/src/go.dbscheme
[1/1] FAILED(UPGRADE) /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.qlref
0 tests passed; 1 tests failed:
FAILED: /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.qlref

Copy link
Contributor

Choose a reason for hiding this comment

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

You need to solve another problem first: I think your installation of CodeQL is too old, leading to the

Unrecoverable mismatch between extractor and library dbschemes.

the test runner is complaining about. So you first need to upgrade to a never version of CodeQL. Alternatively, make sure that your --search-path refers to your check-out of the codeql-go repository (not the codeql repository).

Then, you can use codeql test run --learn to produce a .expected file.

Copy link
Author

Choose a reason for hiding this comment

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

Excellent!after updating codeql, codeql-go and codeql-cli i resolved it and generate .expected file successfully.
But in case UnsafetySignatureGenerate, use codeql test run --learn could pass the test, but nothing in .expected file
is it right? ( others case have result in .expected file)
command result here:

x@x codeql-go % ~/code-audit/codeql-cli/codeql/codeql test run --learn --search-path=ql ql/test/experimental/UnsafetySignatureGenerate
Executing 1 tests in 1 directories.
Extracting test database in /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate.
Compiling queries in /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate.
Executing tests in /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate.
[1/1 eval 956ms] PASSED /Users/choohan/forgit/mygit/codeql-go/ql/test/experimental/UnsafetySignatureGenerate/UnsafetySignatureGenerate.qlref
All 1 tests passed.

Copy link
Contributor

Choose a reason for hiding this comment

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

But in case UnsafetySignatureGenerate, use codeql test run --learn could pass the test, but nothing in .expected file
is it right?

That means the query produced no results on your test file, which is probably not what you want, and most likely means there is a mistake in your query or in the test file.

@smowton
Copy link
Contributor

smowton commented Sep 14, 2021

Closing because inactive; feel free to reopen if you want or revisit this

@smowton smowton closed this Sep 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants