From 719a044ef248acfc32212ebb3b0999a0b451787c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9lio=20Rocha?= Date: Wed, 4 Mar 2026 11:31:53 +0000 Subject: [PATCH] Add i18n and exotic rules while bumping to 1.16.2 --- .tool_version | 2 +- Dockerfile | 2 +- docs/codacy-rules-exotic.yaml | 28 + docs/codacy-rules-i18n.yaml | 309 +++++++++- docs/codacy-rules.yaml | 8 +- docs/multiple-tests/exotic/patterns.xml | 4 + docs/multiple-tests/exotic/results.xml | 11 + .../exotic/src/ExoticCode01.java | 30 + docs/multiple-tests/i18n/patterns.xml | 14 +- docs/multiple-tests/i18n/results.xml | 271 ++++++++- .../multiple-tests/i18n/src/FalsePositive.jsx | 280 +++++++++ .../i18n/src/Javai18nPotentialsIssues.java | 554 ++++++++++++++++++ .../i18n/src/OrderController.java | 4 + internal/docgen/parsing.go | 1 + internal/tool/command.go | 4 +- internal/tool/command_test.go | 2 +- 16 files changed, 1512 insertions(+), 12 deletions(-) create mode 100644 docs/codacy-rules-exotic.yaml create mode 100644 docs/multiple-tests/exotic/patterns.xml create mode 100644 docs/multiple-tests/exotic/results.xml create mode 100644 docs/multiple-tests/exotic/src/ExoticCode01.java create mode 100644 docs/multiple-tests/i18n/src/FalsePositive.jsx create mode 100644 docs/multiple-tests/i18n/src/Javai18nPotentialsIssues.java diff --git a/.tool_version b/.tool_version index bbb75a1..f21c0ae 100644 --- a/.tool_version +++ b/.tool_version @@ -1 +1 @@ -1.16.1 \ No newline at end of file +1.16.2 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0b5831b..6f9e19a 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG OPENGREP_VERSION=v1.16.1 +ARG OPENGREP_VERSION=v1.16.2 # Build codacy-opengrep wrapper FROM golang:1.23-alpine3.21 as builder diff --git a/docs/codacy-rules-exotic.yaml b/docs/codacy-rules-exotic.yaml new file mode 100644 index 0000000..7a7dc1b --- /dev/null +++ b/docs/codacy-rules-exotic.yaml @@ -0,0 +1,28 @@ +rules: + - id: codacy.generic.sql.exotic.hardcoded-sql-values + severity: WARNING + languages: + - generic + patterns: + - pattern-either: + # Complete SQL queries with hardcoded values + - pattern-regex: "(?i)^[^\n]*(?:SELECT|INSERT|UPDATE|DELETE|FROM)[^\n]*\\b(?:85|4322385|86|4323386|1628302)\\b" + - pattern-regex: "(?i)^[^\n]*(?:SELECT|INSERT|UPDATE|DELETE|FROM)[^\n]*['\"](?:IMO|CVO|SMO|US|FRC)['\"]" + # SQL fragments with WHERE/AND/OR and hardcoded values + - pattern-regex: "(?i)^[^\n]*(?:WHERE|AND|OR|SET|VALUES|IN)\\s+[^\n]*\\b(?:85|4322385|86|4323386|1628302)\\b" + - pattern-regex: "(?i)^[^\n]*(?:WHERE|AND|OR|SET|VALUES|IN)\\s+[^\n]*['\"](?:IMO|CVO|SMO|US|FRC)['\"]" + # Column assignments with hardcoded values + - pattern-regex: "(?i)^[^\n]*(?:org_id|organization_id|language|currency|mode)\\s*=\\s*(?:85|4322385|86|4323386|1628302)\\b" + - pattern-regex: "(?i)^[^\n]*(?:org_id|organization_id|language|currency|mode)\\s*=\\s*['\"](?:IMO|CVO|SMO|US|FRC)['\"]" + - pattern-not-regex: '^\s*(?://|--|/\*|\*)' + message: >- + Hardcoded Language, Currency, or Org_Id values detected in SQL. Avoid hardcoding such values; use parameters or configuration instead. + metadata: + category: security + subcategory: sql + description: Flags hardcoded Language, Currency, or Org_Id values in SQL queries that should be parameterized + technology: + - sql + impact: MEDIUM + confidence: LOW + likelihood: HIGH \ No newline at end of file diff --git a/docs/codacy-rules-i18n.yaml b/docs/codacy-rules-i18n.yaml index cba4477..7d36c36 100644 --- a/docs/codacy-rules-i18n.yaml +++ b/docs/codacy-rules-i18n.yaml @@ -106,6 +106,242 @@ rules: confidence: LOW likelihood: HIGH + - id: codacy.js.i18n.no-hardcoded-confirm-prompt + severity: WARNING + languages: + - js + - ts + patterns: + - pattern-either: + - pattern: confirm("...") + - pattern: window.confirm("...") + - pattern: prompt("...") + - pattern: window.prompt("...") + - pattern-not: confirm(t(...)) + - pattern-not: prompt(t(...)) + message: >- + Avoid hardcoded strings in confirm/prompt dialogs. Use an i18n translation function (e.g., t("key")) with interpolation. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded strings in confirm/prompt dialogs to enforce localization + technology: + - javascript + - typescript + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.js.i18n.no-hardcoded-jsx-user-props + severity: WARNING + languages: + - js + - ts + patterns: + - pattern-either: + - pattern: <$EL placeholder="$STR" ... /> + - pattern: <$EL alt="$STR" ... /> + - pattern: <$EL aria-label="$STR" ... /> + - pattern: <$EL label="$STR" ... /> + - pattern: <$EL title="$STR" ... /> + - metavariable-regex: + metavariable: $STR + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + message: >- + Avoid hardcoded strings in JSX user-facing props. Use an i18n translation function (e.g., t("key")). + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded strings in JSX props like placeholder, alt, aria-label, label, and title + technology: + - javascript + - typescript + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.js.i18n.no-hardcoded-console-error + severity: WARNING + languages: + - js + - ts + patterns: + - pattern: console.error("$MSG") + - metavariable-regex: + metavariable: $MSG + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + message: >- + Avoid hardcoded strings in console.error. Use an i18n translation function (e.g., t("key")). + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in console.error calls + technology: + - javascript + - typescript + impact: MEDIUM + confidence: LOW + likelihood: MEDIUM + + - id: codacy.js.i18n.no-hardcoded-throw-error + severity: WARNING + languages: + - js + - ts + patterns: + - pattern: throw new Error("$MSG") + - metavariable-regex: + metavariable: $MSG + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + message: >- + Avoid hardcoded strings in Error constructors. Use an i18n translation function (e.g., t("key")). + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in Error constructor calls + technology: + - javascript + - typescript + impact: MEDIUM + confidence: LOW + likelihood: MEDIUM + + - id: codacy.java.i18n.no-hardcoded-date-format + severity: WARNING + languages: + - java + pattern-either: + - pattern: new SimpleDateFormat("...") + - pattern: DateTimeFormatter.ofPattern("...") + message: >- + Avoid hardcoded date format patterns. Use DateTimeFormatter.ofLocalizedDate() or DateTimeFormatter.ofLocalizedDateTime() for locale-aware formatting. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded date format patterns that are not locale-aware + technology: + - java + impact: MEDIUM + confidence: HIGH + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-decimal-format + severity: WARNING + languages: + - java + pattern-either: + - pattern: new DecimalFormat("...") + - patterns: + - pattern: String.format("$FMT", ...) + - metavariable-regex: + metavariable: $FMT + regex: '%[0-9.]*[fd]' + message: >- + Avoid hardcoded number format patterns. Use NumberFormat.getInstance(locale) or locale-aware formatting for user-visible numbers. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded decimal format patterns and String.format with numeric format specifiers + technology: + - java + impact: MEDIUM + confidence: MEDIUM + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-exception-message + severity: WARNING + languages: + - java + patterns: + - pattern: throw new $EX("$MSG"); + - metavariable-regex: + metavariable: $MSG + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + - pattern-not: throw new $EX($BUNDLE.getString(...)); + message: >- + Avoid hardcoded strings in exception messages. Use ResourceBundle.getString() or a localization key. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in exception constructors + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-return-string + severity: WARNING + languages: + - java + patterns: + - pattern: return "$STR"; + - metavariable-regex: + metavariable: $STR + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + - pattern-not: return $BUNDLE.getString(...); + message: >- + Avoid returning hardcoded natural language strings. Use ResourceBundle.getString() or a localization key. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings returned from methods + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-string-concat + severity: WARNING + languages: + - java + patterns: + - pattern-either: + - patterns: + - pattern: return "$LIT" + ...; + - metavariable-regex: + metavariable: $LIT + regex: '^[A-Z](?![a-z]+\[)[a-z].*' + - patterns: + - pattern: return ... + "$LIT"; + - metavariable-regex: + metavariable: $LIT + regex: '^\s(?!.*\b(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN)\b).*[a-z]{2}' + - pattern-not: return $BUNDLE.getString(...) + ...; + message: >- + Avoid hardcoded strings in string concatenation for user-facing output. Use ResourceBundle.getString() with MessageFormat. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in return concatenation + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-stringbuilder-append + severity: WARNING + languages: + - java + patterns: + - pattern: $SB.append("$STR"); + - metavariable-regex: + metavariable: $STR + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|ORDER)[^.]*\s[^.]*' + message: >- + Avoid hardcoded natural language strings in StringBuilder.append. Use ResourceBundle.getString() or MessageFormat. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in StringBuilder.append calls + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH + - id: codacy.js.i18n.no-raw-jsx-text severity: WARNING languages: @@ -124,4 +360,75 @@ rules: impact: MEDIUM confidence: LOW likelihood: MEDIUM - \ No newline at end of file + + - id: codacy.java.i18n.no-hardcoded-map-put + severity: WARNING + languages: + - java + patterns: + - pattern: $MAP.put("$KEY", "$VALUE"); + - metavariable-regex: + metavariable: $VALUE + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + - pattern-not: $MAP.put("$KEY", $BUNDLE.getString(...)); + message: >- + Avoid hardcoded strings in Map.put(). Use ResourceBundle.getString() or a localization key for user-facing messages. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in Map.put() calls that should be localized + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-map-of + severity: WARNING + languages: + - java + patterns: + - pattern-either: + - pattern: Map.of(..., "$VALUE", ...) + - pattern: Map.of("$KEY", "$VALUE") + - metavariable-regex: + metavariable: $VALUE + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + - pattern-not: Map.of(..., $BUNDLE.getString(...), ...) + message: >- + Avoid hardcoded strings in Map.of(). Use ResourceBundle.getString() or a localization key for user-facing messages. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in Map.of() calls that should be localized + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH + + - id: codacy.java.i18n.no-hardcoded-response-body + severity: WARNING + languages: + - java + patterns: + - pattern-either: + - pattern: ResponseEntity.ok(Map.of(..., "$VALUE", ...)) + - pattern: ResponseEntity.status(...).body(Map.of(..., "$VALUE", ...)) + - pattern: ResponseEntity.$METHOD(Map.of(..., "$VALUE", ...)) + - metavariable-regex: + metavariable: $VALUE + regex: '^(?!SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|ERR)[^.]*\s[^.]*' + - pattern-not: ResponseEntity.ok(Map.of(..., $BUNDLE.getString(...), ...)) + - pattern-not: ResponseEntity.status(...).body(Map.of(..., $BUNDLE.getString(...), ...)) + message: >- + Avoid hardcoded strings in ResponseEntity body maps. Use ResourceBundle.getString() or a localization key for user-facing messages. + metadata: + category: codestyle + subcategory: i18n + description: Flags hardcoded natural language strings in Spring ResponseEntity responses that should be localized + technology: + - java + impact: MEDIUM + confidence: LOW + likelihood: HIGH diff --git a/docs/codacy-rules.yaml b/docs/codacy-rules.yaml index 187a6f2..e983a00 100644 --- a/docs/codacy-rules.yaml +++ b/docs/codacy-rules.yaml @@ -182,7 +182,7 @@ rules: GRANT SELECT privileges should only be given to role-based accounts (ending in '_role'). Direct grants to users or non-role accounts violate security best practices. - pattern-regex: GRANT\s+(DELETE|INSERT|SELECT|UPDATE)(\s*,\s*(DELETE|INSERT|SELECT|UPDATE))*\s+ON\s+[a-zA-Z0-9_]+(\.[a-zA-Z0-9_*]+)?\s+TO\s+(?![a-zA-Z0-9_]*_role\b)[a-zA-Z0-9_]+ + pattern-regex: GRANT\s+(?:DELETE|INSERT|SELECT|UPDATE)(?:\s*,\s*(?:DELETE|INSERT|SELECT|UPDATE))*\s+ON\s+[\w.*]+\s+TO\s+(?!\w*_[Rr][Oo][Ll][Ee]\b)\b\w+ paths: include: - "*.sql" @@ -303,9 +303,9 @@ rules: - generic patterns: - pattern-either: - - pattern-regex: "(?i)\\b\\w*language\\w*\\b\\s*(=|:=)\\s*'?\\b[A-Z]{2}\\b'?" - - pattern-regex: "(?i)\\b\\w*currency\\w*\\b\\s*(=|:=)\\s*'?\\b[A-Z]{3}\\b'?" - - pattern-regex: "(?i)\\b(\\w*\\.)?org_id\\b\\s*(=|:=|IN|!=|<>)\\s*(\\(?\\s*'?\\d+'?(,\\s*'?\\d+'?)*\\s*\\)?)?" + - pattern-regex: "(?i)^(?:(?!--).)*\\b\\w*language\\w*\\b\\s*(=|:=)\\s*'?\\b[A-Z]{2}\\b'?" + - pattern-regex: "(?i)^(?:(?!--).)*\\b\\w*currency\\w*\\b\\s*(=|:=)\\s*'?\\b[A-Z]{3}\\b'?" + - pattern-regex: "(?i)^(?:(?!--).)*\\b(\\w*\\.)?org_id\\b\\s*(=|:=|IN|!=|<>)\\s*(\\(?\\s*'?\\d+'?(,\\s*'?\\d+'?)*\\s*\\)?)?" paths: include: - "*.sql" diff --git a/docs/multiple-tests/exotic/patterns.xml b/docs/multiple-tests/exotic/patterns.xml new file mode 100644 index 0000000..6b5fcb5 --- /dev/null +++ b/docs/multiple-tests/exotic/patterns.xml @@ -0,0 +1,4 @@ + + + + diff --git a/docs/multiple-tests/exotic/results.xml b/docs/multiple-tests/exotic/results.xml new file mode 100644 index 0000000..1b739b9 --- /dev/null +++ b/docs/multiple-tests/exotic/results.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/docs/multiple-tests/exotic/src/ExoticCode01.java b/docs/multiple-tests/exotic/src/ExoticCode01.java new file mode 100644 index 0000000..750f0f7 --- /dev/null +++ b/docs/multiple-tests/exotic/src/ExoticCode01.java @@ -0,0 +1,30 @@ +public class ExoticCode01 { + public void example() { + // This is an exotic code snippet with a unique pattern + } + + + public String buildUserQuery(String tableName, String username) { + // Initialize StringBuilder with a starting capacity or initial string + StringBuilder query = new StringBuilder("SELECT * FROM "); + + // Dynamically append parts of the query + query.append(tableName); + query.append(" WHERE status = 'ACTIVE'"); + + // ❌ org_id should not be hardcoded + query.append(" AND org_id = 85"); + // query.append(" AND org_id = 85"); + query.append(" AND language = 'FRC'"); + + if (username != null && !username.isEmpty()) { + query.append(" AND username = '"); + query.append(username); + query.append("'"); + } + + query.append(";"); + + return query.toString(); + } +} \ No newline at end of file diff --git a/docs/multiple-tests/i18n/patterns.xml b/docs/multiple-tests/i18n/patterns.xml index 823ff8c..3f7d685 100644 --- a/docs/multiple-tests/i18n/patterns.xml +++ b/docs/multiple-tests/i18n/patterns.xml @@ -1,7 +1,19 @@ + + + + + + + + + - \ No newline at end of file + + + + diff --git a/docs/multiple-tests/i18n/results.xml b/docs/multiple-tests/i18n/results.xml index a1bdcb0..194728d 100644 --- a/docs/multiple-tests/i18n/results.xml +++ b/docs/multiple-tests/i18n/results.xml @@ -45,5 +45,274 @@ message="Avoid using toFixed for user-visible number formatting." severity="warning" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/docs/multiple-tests/i18n/src/FalsePositive.jsx b/docs/multiple-tests/i18n/src/FalsePositive.jsx new file mode 100644 index 0000000..e25feb3 --- /dev/null +++ b/docs/multiple-tests/i18n/src/FalsePositive.jsx @@ -0,0 +1,280 @@ +import React from 'react'; + +// ============================================ +// 1. JSX ELEMENT CONTENT - Should BE flagged +// ============================================ + +function WelcomeComponent() { + return ( +
+

Welcome to our application

+

Please enter your information.

+ + Loading data from server... +
+ ); +} + +// ============================================ +// 2. JSX ELEMENT CONTENT - Should NOT be flagged +// ============================================ + +function CorrectComponent({ userName, t }) { + return ( +
+
{userName}
+
{t('welcome.message')}
+ {count} +

ID

{/* Very short technical text */} +
+ ); +} + +// ============================================ +// 3. JSX USER PROPS - Should BE flagged +// ============================================ + +function FormComponent() { + return ( +
+ + + Company logo + + + + + +
+ ); +} + +// ============================================ +// 4. JSX USER PROPS - Should NOT be flagged +// ============================================ + +function CorrectFormComponent({ t }) { + return ( +
+ + {t('logo.alt')} + +
{/* Very short */} +
+ ); +} + +// ============================================ +// 5. ALERT/CONFIRM/PROMPT - Should BE flagged +// ============================================ + +function AlertComponent() { + function handleDelete() { + alert("Error: File not found"); + + if (confirm("Are you sure you want to delete?")) { + // delete logic + } + + const name = prompt("Please enter your name"); + } + + return ; +} + +// ============================================ +// 6. ALERT/CONFIRM/PROMPT - Should NOT be flagged +// ============================================ + +function CorrectAlertComponent({ t }) { + function handleDelete() { + alert(t('error.fileNotFound')); + + if (confirm(t('confirm.delete'))) { + // delete logic + } + + const name = prompt(t('prompt.enterName')); + } + + return ; +} + +// ============================================ +// 7. CONDITIONAL TEXT - Should BE flagged +// ============================================ + +function ConditionalComponent({ isOnline, count }) { + return ( +
+ {isOnline ? "User is online" : "User is offline"} +

{count > 0 ? "Items available" : "Out of stock"}

+
{hasError ? "An error occurred" : "Success"}
+
+ ); +} + +// ============================================ +// 8. CONDITIONAL TEXT - Should NOT be flagged (technical values) +// ============================================ + +function ConditionalTechnicalComponent({ variant, type, position }) { + return ( +
+ ; +} + +// ============================================ +// 10. CONSOLE.ERROR - Should NOT be flagged +// ============================================ + +function CorrectDataLoadingComponent({ t }) { + async function loadData() { + try { + const data = await fetch('/api/users'); + } catch (error) { + console.error(t('error.dataLoad')); + console.error(error); // Variable + console.error("ERR"); // Very short + } + } + + return ; +} + +// ============================================ +// 11. ERROR CONSTRUCTOR - Should BE flagged +// ============================================ + +function ValidationComponent() { + function validateUser(user) { + if (!user.email) { + throw new Error("Invalid user credentials"); + } + + if (!user.password) { + throw new Error("Password is required"); + } + + try { + // some logic + } catch (e) { + throw new Error("Network request failed"); + } + } + + return ; +} + +// ============================================ +// 12. ERROR CONSTRUCTOR - Should NOT be flagged +// ============================================ + +function CorrectValidationComponent({ t }) { + function validateUser(user) { + if (!user.email) { + throw new Error(t('error.invalidCredentials')); + } + + if (!user.password) { + throw new Error(t('error.passwordRequired')); + } + + // Very short technical error + throw new Error("ERR01"); + } + + return ; +} + +// ============================================ +// 13. NUMBER FORMATTING - Should BE flagged +// ============================================ + +function PriceComponent({ price, quantity }) { + return ( +
+

Price: ${price.toFixed(2)}

+

Quantity: {quantity.toLocaleString()}

+

Total: ${(price * quantity).toFixed(2)}

+ {Number(price).toLocaleString()} +
+ ); +} + +// ============================================ +// 14. NUMBER FORMATTING - Should NOT be flagged +// ============================================ + +function CorrectPriceComponent({ price, t }) { + const formatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD' + }); + + return ( +
+

{formatter.format(price)}

+

{t('price', { value: price })}

+

{price.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}

+
+ ); +} + +// ============================================ +// 15. MIXED EXAMPLES - Should BE flagged +// ============================================ + +function ComplexComponent({ user, count }) { + const handleSave = () => { + if (count === 0) { + alert("No items to save"); + return; + } + + try { + // save logic + console.log("Saved successfully"); + } catch (error) { + console.error("Failed to save data"); + throw new Error("Save operation failed"); + } + }; + + return ( +
+

Welcome back, {user.name}!

+

{count > 0 ? "You have items" : "Cart is empty"}

+ + + Total: ${user.balance.toFixed(2)} +
+ ); +} + +export default ComplexComponent; diff --git a/docs/multiple-tests/i18n/src/Javai18nPotentialsIssues.java b/docs/multiple-tests/i18n/src/Javai18nPotentialsIssues.java new file mode 100644 index 0000000..e89c90a --- /dev/null +++ b/docs/multiple-tests/i18n/src/Javai18nPotentialsIssues.java @@ -0,0 +1,554 @@ +package examples; + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.time.format.DateTimeFormatter; +import java.util.ResourceBundle; + +/** + * Test file for Java i18n Semgrep rules + * Each section tests a specific rule with both positive (should flag) + * and negative (should NOT flag) test cases + */ +public class JavaI18nTest { + + private ResourceBundle bundle = ResourceBundle.getBundle("messages"); + + // ============================================ + // 1. HARDCODED RETURN STRING - Should BE flagged + // ============================================ + + public String getUserStatus() { + return "Invalid username or password"; + } + + public String getEmailError() { + return "Please enter a valid email"; + } + + public String getSuccessMessage() { + return "Your account has been created successfully."; + } + + public String getWelcomeText() { + return "Welcome to our platform!"; + } + + // ============================================ + // 2. HARDCODED RETURN STRING - Should NOT be flagged + // ============================================ + + public String getI18nMessage() { + return bundle.getString("error.login.invalid"); + } + + public String getPropertyKey() { + return "user.email.validation"; + } + + public String getSqlQuery() { + return "SELECT * FROM users WHERE email = ?"; + } + + public String getToString() { + return "User[id=" + id + ", name=" + name + "]"; + } + + public String getTechnicalId() { + return "user_session_id"; + } + + public String getEmptyString() { + return ""; + } + + // ============================================ + // 3. STRING CONCATENATION - Should BE flagged + // ============================================ + + public String getUserGreeting(String userName) { + return "Hello, " + userName; + } + + public String getItemCount(int count) { + return count + " items selected"; + } + + public String getWelcomeMessage(String name) { + return "Welcome back, " + name + "!"; + } + + public String getErrorWithDetails(String detail) { + return "Error occurred: " + detail; + } + + // ============================================ + // 4. STRING CONCATENATION - Should NOT be flagged + // ============================================ + + public String getI18nConcatenation(String userName) { + return bundle.getString("greeting.hello") + userName; + } + + public String getSqlWithParam(String table) { + return "SELECT * FROM " + table + " WHERE active = 1"; + } + + public String getKeyPath(String module) { + return "error." + module + ".invalid"; + } + + public String getToStringConcat() { + return "Order[id=" + orderId + ", total=" + total + "]"; + } + + // ============================================ + // 5. STRINGBUILDER APPEND - Should BE flagged + // ============================================ + + public String buildMessage() { + StringBuilder sb = new StringBuilder(); + sb.append("The process has completed successfully."); + return sb.toString(); + } + + public String buildErrorMessage() { + StringBuilder sb = new StringBuilder(); + sb.append("Error: Invalid input provided."); + sb.append("Please check your data and try again."); + return sb.toString(); + } + + public String buildNotification() { + StringBuilder sb = new StringBuilder(); + sb.append("Your order has been shipped."); + return sb.toString(); + } + + // ============================================ + // 6. STRINGBUILDER APPEND - Should NOT be flagged + // ============================================ + + public String buildSqlQuery() { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT u.id, u.name "); + sb.append("FROM users u "); + sb.append("WHERE u.active = ? "); + sb.append("ORDER BY u.created_date DESC"); + return sb.toString(); + } + + public String buildHql() { + StringBuilder sb = new StringBuilder(); + sb.append("FROM Order o "); + sb.append("JOIN o.customer c "); + sb.append("WHERE o.status = :status"); + return sb.toString(); + } + + public String buildTechnicalString() { + StringBuilder sb = new StringBuilder(); + sb.append("user_"); + sb.append(userId); + sb.append("_session"); + return sb.toString(); + } + + // ============================================ + // 7. DATE FORMATS - Should BE flagged + // ============================================ + + public SimpleDateFormat getDateFormatter1() { + return new SimpleDateFormat("MM/dd/yyyy"); + } + + public SimpleDateFormat getDateFormatter2() { + return new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); + } + + public DateTimeFormatter getDateFormatter3() { + return DateTimeFormatter.ofPattern("yyyy-MM-dd"); + } + + public DateTimeFormatter getDateFormatter4() { + return DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); + } + + // ============================================ + // 8. DATE FORMATS - Should NOT be flagged + // ============================================ + + public DateTimeFormatter getLocalizedDateFormatter() { + return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); + } + + public DateTimeFormatter getLocalizedDateTimeFormatter() { + return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT); + } + + // Comment with date format: "MM/dd/yyyy" - should not be flagged + + // ============================================ + // 9. NUMBER FORMATS - Should BE flagged + // ============================================ + + public DecimalFormat getNumberFormatter1() { + return new DecimalFormat("#,##0.00"); + } + + public DecimalFormat getNumberFormatter2() { + return new DecimalFormat("0.00"); + } + + public String formatPrice(double price) { + return String.format("%.2f", price); + } + + public String formatPercentage(double value) { + return String.format("%.1f%%", value); + } + + // ============================================ + // 10. NUMBER FORMATS - Should NOT be flagged + // ============================================ + + public String getFormattedNumber(double value) { + NumberFormat formatter = NumberFormat.getInstance(locale); + return formatter.format(value); + } + + // Comment with format: "#,##0.00" - should not be flagged + + // ============================================ + // 11. VALIDATION MESSAGE - Should BE flagged + // ============================================ + + public String validateEmail(String email) { + if (email == null || !email.contains("@")) { + return "Please enter a valid email address"; + } + return null; + } + + public String validatePassword(String password) { + if (password.length() < 8) { + return "Password must be at least 8 characters long"; + } + return null; + } + + public String validateAge(int age) { + if (age < 18) { + return "You must be at least 18 years old"; + } + return null; + } + + // ============================================ + // 12. VALIDATION MESSAGE - Should NOT be flagged + // ============================================ + + public String validateEmailCorrect(String email) { + if (email == null || !email.contains("@")) { + return bundle.getString("validation.email.invalid"); + } + return null; + } + + public String validateWithKey(String password) { + if (password.length() < 8) { + return "error.password.tooShort"; + } + return null; + } + + public String validateWithSql() { + return "SELECT COUNT(*) FROM users WHERE email = ?"; + } + + // ============================================ + // 13. ERROR MESSAGE - Should BE flagged + // ============================================ + + public String handleError1() { + return "Error: Unable to process request"; + } + + public String handleError2() { + return "Invalid username format"; + } + + public String handleError3() { + return "Please try again later"; + } + + public String handleWarning() { + return "Warning: This action cannot be undone"; + } + + // ============================================ + // 14. ERROR MESSAGE - Should NOT be flagged + // ============================================ + + public String handleErrorCorrect() { + return bundle.getString("error.processing.failed"); + } + + public String getErrorKey() { + return "error.authentication.invalid"; + } + + public String getErrorCode() { + return "ERR_401"; + } + + // ============================================ + // 15. EXCEPTION MESSAGE - Should BE flagged + // ============================================ + + public void throwException1() { + throw new IllegalArgumentException("Invalid input provided"); + } + + public void throwException2() { + throw new RuntimeException("Operation failed"); + } + + public void throwException3() { + throw new IllegalStateException("User is not authenticated"); + } + + public void throwException4() { + if (data == null) { + throw new NullPointerException("Data cannot be null"); + } + } + + // ============================================ + // 16. EXCEPTION MESSAGE - Should NOT be flagged + // ============================================ + + public void throwExceptionCorrect1() { + throw new IllegalArgumentException(bundle.getString("error.invalid.input")); + } + + public void throwExceptionCorrect2() { + throw new RuntimeException("error.operation.failed"); + } + + public void throwExceptionCorrect3(Exception e) { + throw new RuntimeException(e); // Exception as cause, no message + } + + public void throwExceptionCorrect4() { + throw new IllegalArgumentException("ERR_500"); + } + + // ============================================ + // 17. GREETING MESSAGE - Should BE flagged + // ============================================ + + public String getGreeting1() { + return "Welcome to our application"; + } + + public String getGreeting2() { + return "Dear Customer"; + } + + public String getGreeting3() { + return "Hello, thank you for visiting"; + } + + public String getGreeting4() { + return "Hi there!"; + } + + public String getGreeting5() { + return "Greetings from our team"; + } + + public String getGreeting6() { + return "Congratulations on your purchase"; + } + + // ============================================ + // 18. GREETING MESSAGE - Should NOT be flagged + // ============================================ + + public String getGreetingCorrect() { + return bundle.getString("greeting.welcome"); + } + + // Comment: "Welcome to the system" - should not be flagged + + public void logTest() { + // Test data: "Dear Test User" + System.out.println("Running test"); + } + + public void throwHibernateException() { + // Should NOT flag "Hi" in "HibernateException" + throw new HibernateException("Database connection error"); + } + + // ============================================ + // 19. MEASUREMENT UNITS - Should BE flagged + // ============================================ + + public String getWeight(double weight) { + return weight + " lbs"; + } + + public String getDistance(double distance) { + return distance + " miles"; + } + + public String getHeight(double height) { + return height + " inches"; + } + + public String getVolume(double volume) { + return volume + " gallons"; + } + + public String getMetricWeight(double weight) { + return weight + " kg"; + } + + public String getMetricDistance(double distance) { + return distance + " km"; + } + + public String getMetricHeight(double height) { + return height + " cm"; + } + + public String getMetricVolume(double volume) { + return volume + " liters"; + } + + // ============================================ + // 20. MEASUREMENT UNITS - Should NOT be flagged + // ============================================ + + public String getWeightCorrect(double weight, String unit) { + return MessageFormat.format( + bundle.getString("measurement.weight"), + weight, + unit + ); + } + + // Comment: "Weight: 150 lbs" - should not be flagged + + // ============================================ + // 21. PLURALIZATION - Should BE flagged + // ============================================ + + public String getItemCount1(int count) { + if (count == 1) { + return "1 item"; + } else { + return count + " items"; + } + } + + public String getFileCount(int count) { + if (count == 1) { + return "1 file selected"; + } else { + return count + " files selected"; + } + } + + public String getUserCount(int count) { + if (count == 1) { + return "1 user online"; + } else { + return count + " users online"; + } + } + + // ============================================ + // 22. PLURALIZATION - Should NOT be flagged + // ============================================ + + public String getItemCountCorrect(int count) { + MessageFormat mf = new MessageFormat(bundle.getString("items.count")); + return mf.format(new Object[]{count}); + } + + public String getFileCountCorrect(int count) { + return MessageFormat.format( + bundle.getString("files.selected"), + count + ); + } + + public int getTechnicalCount(int count) { + // Technical counting, not display + if (count == 1) { + return 1; + } else { + return count; + } + } + + // ============================================ + // 23. MIXED EXAMPLES - Should BE flagged + // ============================================ + + public String processOrder(String customerName, int itemCount, double total) { + StringBuilder message = new StringBuilder(); + + // Multiple issues in one method + message.append("Hello, " + customerName + "!"); // Concatenation + + if (itemCount == 1) { + message.append("You have 1 item in your cart."); // Pluralization + } else { + message.append("You have " + itemCount + " items in your cart."); // Pluralization + Concatenation + } + + message.append("Total: $" + String.format("%.2f", total)); // Number format + + if (total > 100) { + message.append("Congratulations! You qualify for free shipping."); // Greeting + hardcoded + } + + return message.toString(); + } + + public String validateAndFormat(String email, double price, int quantity) { + // Validation message + if (email == null || !email.contains("@")) { + return "Error: Please provide a valid email address"; + } + + // Number formatting + DecimalFormat df = new DecimalFormat("#,##0.00"); + String formattedPrice = df.format(price); + + // String concatenation with measurement + return "Order total: " + formattedPrice + " for " + quantity + " kg"; + } + + public void validateUserInput(String username, int age) { + // Exception messages + if (username == null || username.isEmpty()) { + throw new IllegalArgumentException("Username cannot be empty"); + } + + if (age < 18) { + throw new IllegalStateException("User must be at least 18 years old"); + } + + // Date formatting + SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy"); + String dateStr = sdf.format(new Date()); + + // Return with greeting + System.out.println("Welcome " + username + ", registered on " + dateStr); + } +} diff --git a/docs/multiple-tests/i18n/src/OrderController.java b/docs/multiple-tests/i18n/src/OrderController.java index 135b18b..be03cd4 100644 --- a/docs/multiple-tests/i18n/src/OrderController.java +++ b/docs/multiple-tests/i18n/src/OrderController.java @@ -1,5 +1,8 @@ // OrderController.java (excerpt with intentional i18n gaps) +public class OrderController { + + @PostMapping public ResponseEntity createOrder(@Valid @RequestBody Order order) { order.setId(idGen.incrementAndGet()); @@ -28,3 +31,4 @@ public ResponseEntity updateOrder(@PathVariable Long id, @Valid @RequestBody // ❌ Hardcoded error message return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("error", "Order not found")); } +} \ No newline at end of file diff --git a/internal/docgen/parsing.go b/internal/docgen/parsing.go index ef7cf09..d8ca208 100644 --- a/internal/docgen/parsing.go +++ b/internal/docgen/parsing.go @@ -121,6 +121,7 @@ func getCodacyRules(docsDir string) (*ParsedSemgrepRules, error) { "codacy-rules.yaml", "codacy-rules-i18n.yaml", "codacy-rules-ai.yaml", + "codacy-rules-exotic.yaml", } for _, file := range customRulesFiles { filePath, _ := filepath.Abs(path.Join(docsDir, file)) diff --git a/internal/tool/command.go b/internal/tool/command.go index 75bd102..299176e 100644 --- a/internal/tool/command.go +++ b/internal/tool/command.go @@ -82,12 +82,12 @@ func createCommandParameters(language string, configurationFile *os.File, filesT "--config", configurationFile.Name(), /// "-l", language, "--timeout", "5", - "--timeout-threshold", "3", + "--timeout-threshold", "50", "--max-target-bytes", "0", "--taint-intrafile", //"--pro", //"--error-recovery", - "--max-memory", "2560", + "--max-memory", "5000", //"-j", strconv.Itoa(runtime.NumCPU()), //"-fast", // adding pro features diff --git a/internal/tool/command_test.go b/internal/tool/command_test.go index eaa94b6..c76e463 100644 --- a/internal/tool/command_test.go +++ b/internal/tool/command_test.go @@ -45,7 +45,7 @@ func TestCreateCommandParameters(t *testing.T) { //"-lang", language, "--config", configurationFile.Name(), "--timeout", "5", - "--timeout-threshold", "3", + "--timeout-threshold", "5", "--max-target-bytes", "0", // "-error_recovery", // "-max_memory", "2560",