diff --git a/src/operators/verify_cc.cc b/src/operators/verify_cc.cc index 1e042f29..75b080ca 100644 --- a/src/operators/verify_cc.cc +++ b/src/operators/verify_cc.cc @@ -15,27 +15,83 @@ #include "operators/verify_cc.h" +#include #include #include "operators/operator.h" + namespace ModSecurity { namespace operators { -bool VerifyCC::evaluate(Assay *assay) { - /** - * @todo Implement the operator VerifyCC. - * Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#verifyCC + +/** + * Luhn Mod-10 Method (ISO 2894/ANSI 4.13) + */ +int VerifyCC::luhnVerify(const char *ccnumber, int len) { + int sum[2] = { 0, 0 }; + int odd = 0; + int digits = 0; + int i; + + /* Weighted lookup table which is just a precalculated (i = index): + * i*2 + (( (i*2) > 9 ) ? -9 : 0) */ - return true; + /* weight lookup table */ + static const int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; + + + /* Add up only digits (weighted digits via lookup table) + * for both odd and even CC numbers to avoid 2 passes. + */ + for (i = 0; i < len; i++) { + if (ccnumber[i] >= (0 + 48) && ccnumber[i] <= (9 + 48)) { + sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); + sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); + odd = 1 - odd; /* alternate weights */ + digits++; + } + } + + /* No digits extracted */ + if (digits == 0) { + return 0; + } + + /* Do a mod 10 on the sum */ + sum[odd] %= 10; + + /* If the result is a zero the card is valid. */ + return sum[odd] ? 0 : 1; } -VerifyCC::VerifyCC(std::string op, std::string param, bool negation) - : Operator() { - this->op = op; - this->param = param; +bool VerifyCC::evaluate(Assay *assay, const std::string &i) { + int offset = 0; + bool is_cc = false; + int target_length = i.length(); + const char *target = i.c_str(); + + for (offset = 0; offset < target_length; offset++) { + std::string shiftedString(i, offset, i.length() - offset); + std::string match; + pcrecpp::StringPiece input(shiftedString); + while (m_re.FindAndConsume(&input, &match)) { + is_cc = luhnVerify(match.c_str(), match.size()); + if (is_cc) { + if (assay) { + assay->debug(9, "CC# match \"" + param + + "\" at " + i + ". [offset " + + std::to_string(offset) + "]"); + } + return true; + } + } + } + + return false; } + } // namespace operators } // namespace ModSecurity diff --git a/src/operators/verify_cc.h b/src/operators/verify_cc.h index 18a2649e..8adf932b 100644 --- a/src/operators/verify_cc.h +++ b/src/operators/verify_cc.h @@ -16,25 +16,31 @@ #ifndef SRC_OPERATORS_VERIFY_CC_H_ #define SRC_OPERATORS_VERIFY_CC_H_ +#include + #include #include "operators/operator.h" -#ifdef __cplusplus namespace ModSecurity { namespace operators { class VerifyCC : public Operator { public: /** @ingroup ModSecurity_Operator */ - VerifyCC(std::string o, std::string p, bool i); - bool evaluate(Assay *assay); + VerifyCC(std::string op, std::string param, bool negation) + : Operator(op, param, negation), + m_re(param, pcrecpp::RE_Options()) { } + + int luhnVerify(const char *ccnumber, int len); + bool evaluate(Assay *assay, const std::string &input) override; + + private: + pcrecpp::RE m_re; }; } // namespace operators } // namespace ModSecurity -#endif - #endif // SRC_OPERATORS_VERIFY_CC_H_ diff --git a/test/test-cases/secrules-language-tests b/test/test-cases/secrules-language-tests index 55534e69..967a8a43 160000 --- a/test/test-cases/secrules-language-tests +++ b/test/test-cases/secrules-language-tests @@ -1 +1 @@ -Subproject commit 55534e6966ebf037db68025eb2b3a14f056cd278 +Subproject commit 967a8a43d1e91b65a05655c0d0a5754bdfa0759a