-
Notifications
You must be signed in to change notification settings - Fork 6
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
Eas 1965 #28
Eas 1965 #28
Conversation
Co-authored-by: Joshua Wong <[email protected]>
…for secret scanner
…cts. And deleted excess debug statements
…VFA with test cases
secretdata.yaml
Outdated
|
||
- package_name: jsonwebtoken | ||
identifier: default | ||
function_name: Some |
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.
shouldn't this be sign
?
match *callee { | ||
[PropPath::Unknown((ref name, ..))] if *name == *"fetch" => Some(Intrinsic::Fetch), | ||
// [PropPath::Def(def)] => {}] |
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.
Don't forget to handle the case where it's just a "regular" function call, i.e. with just an identifier. Otherwise, we wouldn't catch this:
import { sign } from 'jsonwebtoken';
sign('foo', 'secret');
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.
How would you handle a case where sign is not from jsonwebtoken? Like in an instance where a function is names sign but not necessarily imported from jsonwebtoken or it could be defined locally? I think the only way to verify for sure is to check for the specific import of the package. Would calling self.res.is_imported_from
be enough to verify?
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, that would return false
if it's not an import or if it's imported from a different project.
|
||
// testing all the libraries | ||
// var token = jwt.sign({ foo: 'bar' }, 'peek a boo'); | ||
// var hmac = HmacMD5('Secret Message', 'HMAC PASSWORD'); |
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 you add test cases for named imports as well? Also for cases where an identifier could shadow an import to see if we don't raise a false positive there.
…t exports. added new scanning pathway for star imports where prop path is identifier or method with children object imports.
…ed packages. Working on case to catch functions called from packages in nameed imports
…ases and add more tests
false | ||
} | ||
} | ||
_ => 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 shouldn't need this catchall case.
@@ -2981,11 +3503,42 @@ impl Environment { | |||
DefKind::Undefined => DefKind::Undefined, | |||
} | |||
} | |||
pub fn is_import(&self, import_kind: ImportKind, named_import: Option<String>) -> bool { |
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 probably just be a method an ImportKind
, since we don't use the resolver here. Also, this could probably just take in a &str
and check if the import is the same identifier as what was passed in.
// debug!("{named_import}"); | ||
match import_kind { | ||
ImportKind::Star => true, | ||
ImportKind::Default => true, |
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 true iff named_import
is "default", since this function is for checking if the import matches whatever identifier/string we pass in.
…dded more test cases for named imports. todo: add more edge cases and review is_import
…aml. Todo: resolve edge case where same secret scanner vuln got reported twice - after adding jsonwebtoken funtion verify
…s passes in utils.js.
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.
Looks good overall. Mostly stylistic changes asides from moving the match arms.
.gitignore
Outdated
@@ -1 +1,2 @@ | |||
/target | |||
/test-apps |
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 you remove this from the .gitignore
?
_ => false, | ||
} | ||
} | ||
// pub fn is_named_or(&self, import_kind: Option<String>) -> bool { |
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 you remove from this if it's unused?
// case where function requires object to be invoked first | ||
// example: import * as cryptoJS from 'crypto-js'; | ||
// var aes = cryptoJS.AES.encrypt('Secret message', 'secret password'); | ||
if let Some((package_name, import_kind)) = self.res.as_foreign_import(def) { |
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 let Some((package_name, import_kind)) = self.res.as_foreign_import(def) { | |
let (package_name, import_kind) = self.res.as_foreign_import(def)?; |
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.
See this for an explanation of how this works.
|
||
None | ||
} | ||
|
||
[PropPath::Def(def), PropPath::Static(ref s), ..] if is_storage_read(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.
Can you move these two cases above the cases for lowering the secret function intrinsics?
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.
ran into a problem with this one. Moving the last case above all the secret scanner checks led to the secret scanner checks to never get caught.
// Aes.encrypt(blah, blah); | ||
let package_found = self.secret_packages.iter().find(|&package_data| { | ||
if let Some(method) = &package_data.method { | ||
return package_name == package_data.package_name |
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.
return package_name == package_data.package_name | |
package_name == package_data.package_name |
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.
Of course don't forget to remove the semicolon.
@@ -1077,7 +1322,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
match n { | |||
Pat::Ident(BindingIdent { id, .. }) => { | |||
let id = id.to_id(); | |||
let def = self.res.get_or_insert_sym(id, self.module); | |||
let def = self.res.get_or_insert_sym(id.clone(), self.module); |
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.
let def = self.res.get_or_insert_sym(id.clone(), self.module); | |
let def = self.res.get_or_insert_sym(id, self.module); |
_ => {} | ||
} | ||
} | ||
} | ||
} | ||
} else if &ident.sym == "exports" { | ||
} else if ident.sym.to_string() == "exports" { |
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.
} else if ident.sym.to_string() == "exports" { | |
} else if ident.sym == "exports" { |
// adding the default export, so we can resolve it during the lowering | ||
self.res_table | ||
.exported_names | ||
.insert((ident.sym.clone(), self.curr_mod), self.default.unwrap()); |
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 change the ExportCollector::set_default
method to return the DefId
that was set to self.default
at the end of that function?; That way you can do let default_id = self.add_default(DefRes::Undefined, None);
and replace self.default.unwrap()
with default_id
.
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 is no set_default function.
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.
Tldr on my efforts: For this one, I couldn't find set_default
under ExportCollector. Nor did a default_id
variable exist.
So I tried to return defid in add_default. But that also messes up some self.add_default(DefRes::Undefined, None); implementations elsewhere because it's expected that nothing gets returned.
I did try creating self.set_default, but it seemed a little repetitive to call it if add_default is already setting self.default. and calling set_default to return a defId add_default is pretty much the same result as the approach described above.
Otherwise, the easiest way to not use .unwrap() might be to use a let statement grab the value in a Some() object instead of returning it.
Do you want me to break a lil and adapt like other places where self.add_default is called?
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.
Ah, I meant change add_default
to return a DefId. Yeah, that should be fine, since looking at the breakage, it's just match arms that are now returning a DefId
instead of ()
. To fix it, you just need to wrap the arm in braces and add a semicolon.
// adding the default export, so we can resolve it during the lowering | ||
self.res_table.exported_names.insert( | ||
(ident.sym.clone(), self.curr_mod), | ||
self.default.unwrap(), |
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 change the ExportCollector::set_default method to return the DefId that was set to self.default at the end of that function?; That way you can do let default_id = self.add_default(DefRes::Undefined, None); and replace self.default.unwrap() with default_id.
if let Some(mem_expr) = mem_expr_from_assign(n) { | ||
if let MemberProp::Ident(ident_property) = &mem_expr.prop { | ||
if &ident_property.sym == "exports" { | ||
if ident_property.sym.to_string() == "exports" { |
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 ident_property.sym.to_string() == "exports" { | |
if ident_property.sym == "exports" { |
[PropPath::Def(def), ..] => match self.res.is_imported_from(def, "@forge/api") { | ||
Some(ImportKind::Named(ref name)) if *name == *"authorize" => { | ||
Some(Intrinsic::Authorize(IntrinsicName::Other)) | ||
} | ||
_ => None, | ||
}, |
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.
Specifically moving this case above the secret scanner checks I think catches all statements before secret scanner checks.
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.
Ah, mb I thought that match was a guard statement for some reason. If you change it to a guard statement with if self.res.is_imported_from(def, "@forge/api") == *"authorize" => Some(Intrinsic::Authorize(IntrinsicName::Other)),
, then moving it above shouldn't swallow the secret scanner checks.
…match statement logic to now swallow rest of secret checker
Added new pathway for scanning for packages requiring secrets to include * imported packages.
Updated secretdata.yaml with new packages to scan for. Commented out packages use require import method that we don't scan for.
Added package import to DVFA.
Todo: definitely can clean up code from comments and stuff in DVFA and around main. But as_intrinsic in definition.rs is done and cleaned up.