Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Rust: Recognize more sensitive data sources #19470

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

Merged
merged 15 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions 48 rust/ql/lib/codeql/rust/security/SensitiveData.qll
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ private class SensitiveDataFunction extends Function {
/**
* A function call data flow node that might produce sensitive data.
*/
private class SensitiveDataCall extends SensitiveData {
private class SensitiveDataFunctionCall extends SensitiveData {
SensitiveDataClassification classification;

SensitiveDataCall() {
SensitiveDataFunctionCall() {
geoffw0 marked this conversation as resolved.
Show resolved Hide resolved
classification =
this.asExpr()
.getAstNode()
Expand All @@ -53,6 +53,33 @@ private class SensitiveDataCall extends SensitiveData {
override SensitiveDataClassification getClassification() { result = classification }
}

/**
* An enum variant that might produce sensitive data.
*/
private class SensitiveDataVariant extends Variant {
SensitiveDataClassification classification;

SensitiveDataVariant() {
HeuristicNames::nameIndicatesSensitiveData(this.getName().getText(), classification)
}

SensitiveDataClassification getClassification() { result = classification }
}

/**
* An enum variant call data flow node that might produce sensitive data.
*/
private class SensitiveDataVariantCall extends SensitiveData {
SensitiveDataClassification classification;

SensitiveDataVariantCall() {
classification =
this.asExpr().getAstNode().(CallExpr).getVariant().(SensitiveDataVariant).getClassification()
}

override SensitiveDataClassification getClassification() { result = classification }
}

/**
* A variable that might contain sensitive data.
*/
Expand All @@ -67,7 +94,7 @@ private class SensitiveDataVariable extends Variable {
}

/**
* A variable access data flow node that might produce sensitive data.
* A variable access data flow node that might be sensitive data.
*/
private class SensitiveVariableAccess extends SensitiveData {
SensitiveDataClassification classification;
Expand All @@ -84,3 +111,18 @@ private class SensitiveVariableAccess extends SensitiveData {

override SensitiveDataClassification getClassification() { result = classification }
}

/**
* A field access data flow node that might be sensitive data.
*/
private class SensitiveFieldAccess extends SensitiveData {
geoffw0 marked this conversation as resolved.
Show resolved Hide resolved
SensitiveDataClassification classification;

SensitiveFieldAccess() {
exists(FieldExpr fe | fe.getParentNode*() = this.asExpr().getAstNode() |
HeuristicNames::nameIndicatesSensitiveData(fe.getIdentifier().getText(), classification)
)
}

override SensitiveDataClassification getClassification() { result = classification }
}
194 changes: 169 additions & 25 deletions 194 rust/ql/test/library-tests/sensitivedata/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ struct MyStruct {
password: String,
password_file_path: String,
password_enabled: String,
mfa: String,
numfailed: String,
}

impl MyStruct {
Expand All @@ -22,12 +24,13 @@ fn get_password() -> String { get_string() }

fn test_passwords(
password: &str, pass_word: &str, passwd: &str, my_password: &str, password_str: &str,
pass_phrase: &str, passphrase: &str, passPhrase: &str,
auth_key: &str, authkey: &str, authKey: &str, authentication_key: &str, authenticationkey: &str, authenticationKey: &str,
harmless: &str, encrypted_password: &str, password_hash: &str,
pass_phrase: &str, passphrase: &str, passPhrase: &str, backup_code: &str,
auth_key: &str, authkey: &str, authKey: &str, authentication_key: &str, authenticationkey: &str, authenticationKey: &str, oauth: &str,
harmless: &str, encrypted_password: &str, password_hash: &str, passwordFile: &str,
ms: &MyStruct
) {
// passwords

sink(password); // $ sensitive=password
sink(pass_word); // $ MISSING: sensitive=password
sink(passwd); // $ sensitive=password
Expand All @@ -36,29 +39,38 @@ fn test_passwords(
sink(pass_phrase); // $ sensitive=password
sink(passphrase); // $ sensitive=password
sink(passPhrase); // $ sensitive=password
sink(backup_code); // $ MISSING: sensitive=password

sink(auth_key); // $ sensitive=password
sink(authkey); // $ sensitive=password
sink(authKey); // $ sensitive=password
sink(authentication_key); // $ sensitive=password
sink(authenticationkey); // $ sensitive=password
sink(authenticationKey); // $ sensitive=password
sink(oauth); // $ MISSING: sensitive=password

sink(ms); // $ MISSING: sensitive=password
sink(ms.password.as_str()); // $ MISSING: sensitive=password
sink(ms.password.as_str()); // $ sensitive=password
sink(ms.mfa.as_str()); // $ MISSING: sensitive=password

sink(get_password()); // $ sensitive=password
let password2 = get_string();
sink(password2); // $ sensitive=password

let qry = "password=abc";
sink(qry); // $ MISSING: sensitive=password

// not passwords

sink(harmless);
sink(encrypted_password);
sink(password_hash);
sink(passwordFile); // $ SPURIOUS: sensitive=password

sink(ms.harmless.as_str());
sink(ms.password_file_path.as_str());
sink(ms.password_enabled.as_str());
sink(ms.password_file_path.as_str()); // $ SPURIOUS: sensitive=password
sink(ms.password_enabled.as_str()); // $ SPURIOUS: sensitive=password
sink(ms.numfailed.as_str());

sink(get_string());
let harmless2 = get_string();
Expand All @@ -75,10 +87,11 @@ fn get_next_token() -> String { get_string() }
fn test_credentials(
account_key: &str, accnt_key: &str, license_key: &str, secret_key: &str, is_secret: bool, num_accounts: i64,
username: String, user_name: String, userid: i64, user_id: i64, my_user_id_64: i64, unique_id: i64, uid: i64,
sessionkey: &[u64; 4], session_key: &[u64; 4], hashkey: &[u64; 4], hash_key: &[u64; 4],
sessionkey: &[u64; 4], session_key: &[u64; 4], hashkey: &[u64; 4], hash_key: &[u64; 4], sessionkeypath: &[u64; 4], account_key_path: &[u64; 4],
ms: &MyStruct
) {
// credentials

sink(account_key); // $ sensitive=id
sink(accnt_key); // $ sensitive=id
sink(license_key); // $ MISSING: sensitive=secret
Expand All @@ -101,12 +114,15 @@ fn test_credentials(
sink(get_secret_token()); // $ sensitive=secret

// not (necessarily) credentials

sink(is_secret);
sink(num_accounts); // $ SPURIOUS: sensitive=id
sink(unique_id);
sink(uid); // $ SPURIOUS: sensitive=id
sink(hashkey);
sink(hash_key);
sink(sessionkeypath); // $ SPURIOUS: sensitive=id
sink(account_key_path); // $ SPURIOUS: sensitive=id

sink(ms.get_certificate_url()); // $ SPURIOUS: sensitive=certificate
sink(ms.get_certificate_file()); // $ SPURIOUS: sensitive=certificate
Expand All @@ -115,58 +131,186 @@ fn test_credentials(
sink(get_next_token());
}

struct MacAddr {
values: [u8;12],
}

struct DeviceInfo {
api_key: String,
deviceApiToken: String,
finger_print: String,
ip_address: String,
macaddr12: [u8;12],
mac_addr: MacAddr,
networkMacAddress: String,

// not private device info
macro_value: bool,
mac_command: u32,
skip_address: String,
}

impl DeviceInfo {
fn test_device_info(&self, other: &DeviceInfo) {
// private device info

sink(&self.api_key); // $ MISSING: sensitive=id
sink(&other.api_key); // $ MISSING: sensitive=id
sink(&self.deviceApiToken); // $ MISSING: sensitive=id
sink(&self.finger_print); // $ MISSING: sensitive=id
sink(&self.ip_address); // $ MISSING: sensitive=id
sink(self.macaddr12); // $ MISSING: sensitive=id
sink(&self.mac_addr); // $ MISSING: sensitive=id
sink(self.mac_addr.values); // $ MISSING: sensitive=id
sink(self.mac_addr.values[0]); // $ MISSING: sensitive=id
sink(&self.networkMacAddress); // $ MISSING: sensitive=id

// not private device info

sink(self.macro_value);
sink(self.mac_command);
sink(&self.skip_address);
}
}

struct Financials {
harmless: String,
my_bank_account_number: String,
credit_card_no: String,
credit_rating: i32,
user_ccn: String
user_ccn: String,
cvv: String,
beneficiary: String,
routing_number: u64,
routingNumberText: String,
iban: String,
iBAN: String,

num_accounts: i32,
total_accounts: i32,
accounting: i32,
unaccounted: bool,
multiband: bool,
}

enum Gender {
Male,
Female,
}

struct SSN {
data: u128,
}

impl SSN {
fn get_data(&self) -> u128 {
return self.data;
}
}

struct MyPrivateInfo {
mobile_phone_num: String,
contact_email: String,
contact_e_mail_2: String,
emergency_contact: String,
my_ssn: String,
ssn: SSN,
birthday: String,
emergency_contact: String,
name_of_employer: String,

gender: Gender,
genderString: String,

patient_id: u64,
linkedPatientId: u64,
patient_record: String,
medical_notes: Vec<String>,
confidentialMessage: String,

latitude: f64,
longitude: Option<f64>,

financials: Financials
}

enum ContactDetails {
HomePhoneNumber(String),
MobileNumber(String),
Email(String),
FavouriteColor(String),
}

fn test_private_info(
info: &MyPrivateInfo
info: &MyPrivateInfo, details: &ContactDetails,
) {
// private info
sink(info.mobile_phone_num.as_str()); // $ MISSING: sensitive=private
sink(info.mobile_phone_num.to_string()); // $ MISSING: sensitive=private

sink(info.mobile_phone_num.as_str()); // $ sensitive=private
sink(info.mobile_phone_num.to_string()); // $ sensitive=private
sink(info.contact_email.as_str()); // $ MISSING: sensitive=private
sink(info.contact_e_mail_2.as_str()); // $ MISSING: sensitive=private
sink(info.my_ssn.as_str()); // $ MISSING: sensitive=private
sink(info.birthday.as_str()); // $ MISSING: sensitive=private
sink(info.emergency_contact.as_str()); // $ MISSING: sensitive=private
sink(info.name_of_employer.as_str()); // $ MISSING: sensitive=private
sink(info.my_ssn.as_str()); // $ sensitive=private
sink(&info.ssn); // $ sensitive=private
sink(info.ssn.data); // $ sensitive=private
sink(info.ssn.get_data()); // $ sensitive=private
sink(info.birthday.as_str()); // $ sensitive=private
sink(info.emergency_contact.as_str()); // $ sensitive=private
sink(info.name_of_employer.as_str()); // $ sensitive=private

sink(&info.medical_notes); // $ MISSING: sensitive=private
sink(info.medical_notes[0].as_str()); // $ MISSING: sensitive=private
sink(&info.gender); // $ MISSING: sensitive=private
sink(info.genderString.as_str()); // $ MISSING: sensitive=private
let sex = "Male";
let gender = Gender::Female;
let a = Gender::Female;
sink(sex); // $ MISSING: sensitive=private
sink(gender); // $ MISSING: sensitive=private
sink(a); // $ MISSING: sensitive=private

sink(info.patient_id); // $ MISSING: sensitive=private
sink(info.linkedPatientId); // $ MISSING: sensitive=private
sink(info.patient_record.as_str()); // $ MISSING: sensitive=private
sink(info.patient_record.trim()); // $ MISSING: sensitive=private
sink(&info.medical_notes); // $ sensitive=private
sink(info.medical_notes[0].as_str()); // $ sensitive=private
for n in info.medical_notes.iter() {
sink(n.as_str()); // $ MISSING: sensitive=private
sink(n.as_str()); // $ sensitive=private
}
sink(info.confidentialMessage.as_str()); // $ MISSING: sensitive=private
sink(info.confidentialMessage.to_lowercase()); // $ MISSING: sensitive=private

sink(info.latitude); // $ MISSING: sensitive=private
sink(info.latitude); // $ sensitive=private
let x = info.longitude.unwrap();
sink(x); // $ MISSING: sensitive=private
sink(x); // $ sensitive=private

sink(info.financials.my_bank_account_number.as_str()); // $ MISSING: sensitive=private
sink(info.financials.credit_card_no.as_str()); // $ MISSING: sensitive=private
sink(info.financials.credit_rating); // $ MISSING: sensitive=private
sink(info.financials.user_ccn.as_str()); // $ MISSING: sensitive=private
sink(info.financials.my_bank_account_number.as_str()); // $ sensitive=private SPURIOUS: sensitive=id
sink(info.financials.credit_card_no.as_str()); // $ sensitive=private
sink(info.financials.credit_rating); // $ sensitive=private
sink(info.financials.user_ccn.as_str()); // $ sensitive=private
sink(info.financials.cvv.as_str()); // $ MISSING: sensitive=private
sink(info.financials.beneficiary.as_str()); // $ MISSING: sensitive=private
sink(info.financials.routing_number); // $ MISSING: sensitive=private
sink(info.financials.routingNumberText.as_str()); // $ MISSING: sensitive=private
sink(info.financials.iban.as_str()); // $ MISSING: sensitive=private
sink(info.financials.iBAN.as_str()); // $ MISSING: sensitive=private

sink(ContactDetails::HomePhoneNumber("123".to_string())); // $ sensitive=private
sink(ContactDetails::MobileNumber("123".to_string())); // $ sensitive=private
sink(ContactDetails::Email("a@b".to_string())); // $ MISSING: sensitive=private
if let ContactDetails::MobileNumber(num) = details {
sink(num.as_str()); // $ MISSING: sensitive=private
}

// not private info

let modulesEx = 1;
sink(modulesEx);

sink(info.financials.harmless.as_str());
sink(info.financials.num_accounts); // $ SPURIOUS: sensitive=id
sink(info.financials.total_accounts); // $ SPURIOUS: sensitive=id
sink(info.financials.accounting); // $ SPURIOUS: sensitive=id
sink(info.financials.unaccounted); // $ SPURIOUS: sensitive=id
sink(info.financials.multiband);

sink(ContactDetails::FavouriteColor("blue".to_string()));
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.