Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions sds/src/scanner/regex_rule/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub enum SecondaryValidator {
IbanChecker,
IrishPpsChecksum,
ItalianNationalIdChecksum,
JapanCorporateNumberChecksum,
JapanDrivingLicenseChecksum,
JwtClaimsValidator {
config: JwtClaimsValidatorConfig,
Expand Down
92 changes: 92 additions & 0 deletions sds/src/secondary_validation/japan_corporate_number_checksum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use crate::secondary_validation::{Validator, get_next_digit};

pub struct JapanCorporateNumberChecksum;

impl Validator for JapanCorporateNumberChecksum {
fn is_valid_match(&self, regex_match: &str) -> bool {
if regex_match.len() != 13 || !regex_match.bytes().all(|byte| byte.is_ascii_digit()) {
return false;
}

let mut chars = regex_match.chars();
let Some(check_digit) = get_next_digit(&mut chars) else {
return false;
};
let mut sum = 0;
for weight in [2, 1].into_iter().cycle().take(12) {
let Some(digit) = get_next_digit(&mut chars) else {
return false;
};
sum += digit * weight;
}

// NTA: https://www.houjin-bangou.nta.go.jp/documents/houjinbangounokousei.pdf
check_digit == 9 - sum % 9
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn accepts_official_examples() {
// NTA worked example: https://www.houjin-bangou.nta.go.jp/documents/checkdigit.pdf
assert!(JapanCorporateNumberChecksum.is_valid_match("8700110005901"));
// NTA API sample: https://www.houjin-bangou.nta.go.jp/pc/download/images/k-sample-explain-r0404.pdf
assert!(JapanCorporateNumberChecksum.is_valid_match("8040001999013"));
}

#[test]
fn handles_every_remainder() {
// Synthetic bases: only the units digit varies, giving sums 0 through 8.
for (units, check_digit) in (0..=8).zip((1..=9).rev()) {
let number = format!("{check_digit}00000000000{units}");
assert!(
JapanCorporateNumberChecksum.is_valid_match(&number),
"{number}"
);
}
// Synthetic base with a tens digit of 1 gives sum 2.
assert!(JapanCorporateNumberChecksum.is_valid_match("7000000000010"));
}

#[test]
fn rejects_wrong_check_digits() {
for check_digit in b'0'..=b'9' {
let mut number = *b"8700110005901";
number[0] = check_digit;
let number = std::str::from_utf8(&number).unwrap();
assert_eq!(
JapanCorporateNumberChecksum.is_valid_match(number),
check_digit == b'8',
"{number}"
);
}
assert!(!JapanCorporateNumberChecksum.is_valid_match("8700110005902"));
}

#[test]
fn rejects_malformed_input() {
for number in [
"",
"870011000590",
"87001100059010",
"870011000590A",
"8 700110005901",
"8-700110005901",
" 8700110005901",
"8700110005901 ",
"8700110005901",
"8700110005901",
"87001100059é",
"1234567890123",
"9876543210987",
] {
assert!(
!JapanCorporateNumberChecksum.is_valid_match(number),
"{number}"
);
}
}
}
5 changes: 5 additions & 0 deletions sds/src/secondary_validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod iban_checker;
mod irish_pps_checksum;
mod iso_7064_checksum;
mod italian_national_id_checksum;
mod japan_corporate_number_checksum;
mod japan_driving_license_checksum;
pub mod jwt_claims_validator;
mod jwt_expiration_checker;
Expand Down Expand Up @@ -101,6 +102,7 @@ pub use crate::secondary_validation::iso_7064_checksum::{
Mod97_10checksum, Mod661_26checksum, Mod1271_36Checksum,
};
pub use crate::secondary_validation::italian_national_id_checksum::ItalianNationalIdChecksum;
pub use crate::secondary_validation::japan_corporate_number_checksum::JapanCorporateNumberChecksum;
pub use crate::secondary_validation::japan_driving_license_checksum::JapanDrivingLicenseChecksum;
pub use crate::secondary_validation::jwt_claims_validator::JwtClaimsValidator;
pub use crate::secondary_validation::jwt_expiration_checker::JwtExpirationChecker;
Expand Down Expand Up @@ -249,6 +251,9 @@ impl SecondaryValidator {
SecondaryValidator::IbanChecker => Arc::new(IbanChecker),
SecondaryValidator::IrishPpsChecksum => Arc::new(IrishPpsChecksum),
SecondaryValidator::ItalianNationalIdChecksum => Arc::new(ItalianNationalIdChecksum),
SecondaryValidator::JapanCorporateNumberChecksum => {
Arc::new(JapanCorporateNumberChecksum)
}
SecondaryValidator::JapanDrivingLicenseChecksum => {
Arc::new(JapanDrivingLicenseChecksum)
}
Expand Down
Loading