-
Notifications
You must be signed in to change notification settings - Fork 125
Add queries for CORS checking, Cookie attribute checking and unsafe signature generation #307
Conversation
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.
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 |
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 be @kind problem
; see https://github.com/github/codeql/blob/master/docs/query-metadata-style-guide.md.
( | ||
( | ||
c = f.getACall() | ||
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"") |
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 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
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("\"*\"") |
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.
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" |
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 alert message doesn't match our usual style. I would suggest you try and rephrase it in a more neutral tone, perhaps something like
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." |
// f.getACall().getArgument(0).toString().matches("\"Access-Control-Allow-Credentials\"") | ||
// and f.getACall().getArgument(1).toString().matches("\"true\"") | ||
// and c = f.getACall() |
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.
// 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" |
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.
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\"") |
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.
//c.getArgument(0).toString().matches("\"Access-Control-Allow-Origin\"") |
and c.getArgument(0).toString().matches("\"Access-Control-Allow-Methods\"") | ||
and c.getArgument(1).toString().regexpMatch("(?i).*?(put|head|delete|options).*?") |
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.
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" |
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.
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 |
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.
Alert queries should select two things: a program element, and an alert message.
select "Usage here: ", c, c.asExpr().getLocation(), s | |
select c, s |
thanks for your help !, i just fix it and commit again |
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! 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() |
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.
Could you rename this variable, please?
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.
oh sorry i forgot to change it
@@ -0,0 +1,23 @@ | |||
<!DOCTYPE qhelp PUBLIC |
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.
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
.
ql/src/experimental/CookieAttributesChecking/CookieAttributesChecking.ql
Show resolved
Hide resolved
import go | ||
from Function f, StructLit s, int x, string infor, string tmp | ||
where | ||
f.getName() = "SetCookie" |
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.
It seems like you're not actually using this variable for anything.
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.
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
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 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.
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.
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" |
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.
and s.getType().toString() = "Cookie" | |
and s.getType().hasQualifiedName("net/http", "Cookie") |
@@ -0,0 +1,32 @@ | |||
<!DOCTYPE qhelp PUBLIC |
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 don't understand what this query does.
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 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¶2=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
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 sounds interesting. Do you have a reference for this type of attack?
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.
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
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.
Can't find a better reference than https://crypto.stackexchange.com/questions/55162/best-way-to-hash-two-values-into-one at the moment
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.
@smowton can't agree any more
and | ||
call2.getCalleeName() = "Write" | ||
and | ||
source.asExpr() = f1.getACall().asExpr() |
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.
Why not just source = f1.getACall()
?
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.
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()) |
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.
call1.toString().matches(a1.getRhs().toString()) | |
a1.getRhs() = call1.asExpr() |
and | ||
call1.toString().matches(a1.getRhs().toString()) | ||
and | ||
call2.getReceiver().toString() = a1.getLhs().toString() |
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.
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 |
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.
Similar to above: use Variable
or data flow.
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." |
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.
Message doesn't match header
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.
what about this
Allowing Access-Control-Allow-Credentials as true would expand you attack surface if CORS attacks appears
and | ||
( | ||
( | ||
c = f.getACall() |
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.
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." |
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.
Allowing cross-origin HTTP methods
( | ||
c = f.getACall() | ||
and c.getArgument(0).getStringValue() = "Access-Control-Allow-Origin" | ||
and c.getArgument(1).getStringValue() = "*" |
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.
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" |
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.
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.
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 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 |
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 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") |
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 concept applies just as well for different hash algorithms
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.
yes, but in most code, i found they use sha256. Later could expand the range of the hash algorithms
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.
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 |
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.
Rather than specify these exact sources, you might want to use UntrustedFlowSource, a class representing generally data supplied by the user.
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.
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
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 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 |
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.
Can't find a better reference than https://crypto.stackexchange.com/questions/55162/best-way-to-hash-two-values-into-one at the moment
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.
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.
tmp = concat(int i| i in [0..s.getNumElement()]|s.getKey(i).(Ident).getName(), "|") | ||
and not | ||
tmp.matches("%HttpOnly%") |
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.
No need for this level of trickery. You can simply do
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
.
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.
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
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.
to autoformat the ql file, using codeql query format file
?
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.
That's right, yes.
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.
(And don't forget to pass -i
to make it update the file in place.)
a1.getRhs() = call1.asExpr() | ||
and | ||
DataFlow::localFlow(call1, call2.getReceiver()) |
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.
Now that you are using localFlow
you don't need a1
above any more.
and | ||
DataFlow::localFlow(call1, call2.getReceiver()) | ||
and | ||
call2.getCalleeName() = "Write" |
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.
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()) |
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.
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 |
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.
Slightly awkward name; perhaps change to something like
* @name Unsafety Signature Generate checking | |
* @name Unsafe signature calculation |
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.
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 |
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.
What is the role of c1
? It does not seem to have anything to do with source
.
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.
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
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.
So are you planning on deleting this line?
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 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
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 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() |
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.
Why getASuccessor()
?
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.
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
)
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 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.
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.
sounds great, let me try
ql/src/experimental/UnsafetySignatureGenerate/UnSafetySignatureGenerate.ql
Outdated
Show resolved
Hide resolved
ql/src/experimental/CookieAttributesChecking/CookieAttributesChecking.ql
Show resolved
Hide resolved
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetFloat") or | ||
m.hasQualifiedName("github.com/astaxie/beego.Controller", "GetBool") | ||
) and | ||
c1.getCalleeName() = "Write" and |
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.
So are you planning on deleting this line?
) and | ||
c1.getCalleeName() = "Write" and | ||
DataFlow::localFlow(m.getACall(), d) and | ||
source = d.getASuccessor() |
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 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.
ql/src/experimental/UnsafetySignatureGenerate/UnSafetySignatureGenerate.ql
Outdated
Show resolved
Hide resolved
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。" |
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.
"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." |
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 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.
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)
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.
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。" |
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 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 |
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 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 |
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.
You could also change the type of b
to DataFlow::EqualityTestNode
.
b.getAnOperand().toString() = a1.getAnLhs().toString() and | ||
b.getAnOperand().toString() = a.getAnLhs().toString() |
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.
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())
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.
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, |
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 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 = "" { |
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.
if a == "" && b = "" { | |
if a == "" && b == "" { |
2. add metadata of UnSafeSignatureGenerate.ql 3. use DataFlow::EqualityTestNode instead 4. use a nicer parser
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 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 |
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 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." |
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.
"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." |
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 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
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 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.
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, 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?
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.
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
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.
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.
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.
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.
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.
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.
Closing because inactive; feel free to reopen if you want or revisit this |
hello, i write some security rules of go in ql,
1、CORSChecking
2、CookieAttributesChecking
3、UnSafetySignatureGenerate