Skip to content

Conversation

@MuralitharanK
Copy link
Owner

@MuralitharanK MuralitharanK commented Dec 27, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added comprehensive print service with card generation, PDF creation, and QR code capabilities
    • Introduced event-driven printing workflow with WebSub integration
    • Added digital signature and encryption support for secure document handling
    • Implemented template-based document generation
    • Added biometric data processing capabilities
  • Chores

    • Updated dependencies and logging infrastructure
    • Updated configuration and property management
    • Added automated Docker image build and push workflow

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 27, 2025

📝 Walkthrough

Walkthrough

This pull request establishes a new standalone print service module, migrating from kernel-based implementations to a self-contained io.mosip.print namespace. It introduces comprehensive card generation, PDF manipulation, cryptographic decryption, QR code generation, and template management capabilities with corresponding service interfaces, implementations, exception hierarchies, and configuration infrastructure.

Changes

Cohort / File(s) Summary
CI/CD & Build Configuration
.github/workflows/manual_push_trigger.yml, pom.xml
Adds GitHub Actions workflow for Maven builds with GPG/OSSRH publishing and Docker image creation; updates parent Spring Boot version, modernizes dependencies (Spring Cloud Config, ActiveMQ, PostgreSQL, JAXB runtime, iText7, ZXing, JWT, crypto libraries), removes kernel dependencies.
Application Bootstrap & Auto-Configuration
src/main/java/io/mosip/print/PrintPDFApplication.java
Excludes SecurityAutoConfiguration, DataSourceAutoConfiguration, HibernateJpaAutoConfiguration, CacheAutoConfiguration from Spring Boot auto-configuration; imports CbeffUtil/CbeffImpl from new print package.
Constant Enums & Fields
src/main/java/io/mosip/print/constant/*.java (ApiName, CbeffConstant, DateUtilConstants, HubMode, PDFGeneratorExceptionCodeConstant, PrintTransactionStatus, QrVersion, QrcodeConstants, QrcodeExceptionConstants, TemplateManagerConstant, TemplateManagerExceptionCodeConstant, UINCardConstant, WebSubClientConstants, WebSubClientErrorCode)
Introduces 13 new constant/enum classes defining error codes, format identifiers, QR versions (V1–V40), hub modes, transaction statuses, and UI field mappings; centralizes standardized error messaging and domain constants.
Core HTTP Wrappers
src/main/java/io/mosip/print/core/http/RequestWrapper.java, ResponseWrapper.java
Relocates from io.mosip.registration.print.core.http to io.mosip.print.core.http namespace; maintains generic request/response envelope structure.
DTOs (Data Transfer Objects)
src/main/java/io/mosip/print/dto/*.java (BaseRequestDTO, BaseResponseDTO, CredentialRequestDto, CredentialResponseDto, CryptoWithPinRequestDto, CryptoWithPinResponseDto, DataShare, DataShareResponseDto, Errors, IdResponseDTO1, MQResponseDto, PrintMQData, PrintMQDetails, PrintStatusRequestDto, SignatureCertificate)
Defines 15 new DTOs for credential exchange, crypto operations, data sharing, print status tracking, and message queue communication; integrates Lombok for boilerplate generation.
Entities & Repositories
src/main/java/io/mosip/print/entity/MspCardEntity.java, src/main/java/io/mosip/print/repository/MspCardRepository.java
Introduces JPA entity for MSP card persistence (msp_card table); provides Spring Data repository with basic CRUD operations.
Exception Hierarchy
src/main/java/io/mosip/print/exception/*.java (BaseCheckedException, BaseUncheckedException, InfoItem, ExceptionUtils, + 20 specialized exception types)
Establishes custom exception base classes with error code/message aggregation; provides 20+ domain-specific exception types (BiometricException, CbeffException, DataShareException, PDFGeneratorException, QrcodeGenerationException, WebSubClientException, etc.) with standardized error reporting; adds PlatformErrorMessages enum with new error code entries.
Service Controller
src/main/java/io/mosip/print/controller/Print.java
Defines REST endpoint for WebSub callbacks; removes legacy enqueue endpoint; simplifies handleSubscribeEvent to invoke printService.generateCard(eventModel) and return success response.
Core Service Interfaces (SPIs)
src/main/java/io/mosip/print/spi/*.java (CbeffUtil, IBioApi, PDFGenerator, QrCodeGenerator, TemplateManager, TemplateManagerBuilder)
Introduces 6 service provider interfaces defining contracts for CBEFF XML processing, biometric quality/matching, PDF generation/signing, QR code creation, and Velocity template merging.
Service Implementations – CBEFF & Biometrics
src/main/java/io/mosip/print/service/impl/CbeffContainerImpl.java, CbeffImpl.java, BioApiImpl.java
Implements CBEFF BIR container creation/updating/validation; loads XSD from config server; extracts biometric data from XML; provides BioAPI implementations for quality checking, matching, and template extraction using in-memory scoring.
Service Implementations – PDF & Template
src/main/java/io/mosip/print/service/impl/PDFGeneratorImpl.java, TemplateManagerImpl.java, TemplateManagerBuilderImpl.java
Generates PDFs from HTML/InputStream via iText/HtmlConverter; supports merging, signing, and encryption; renders Velocity templates via VelocityEngine with configurable resource paths and caching.
Service Implementations – QR & Print
src/main/java/io/mosip/print/service/impl/QrcodeGeneratorImpl.java, PrintImpl.java, PrintCardImpl.java, PrintDBService.java, PrintRepository.java
Generates QR codes via ZXing with version/data validation; persists print card records via JPA/repository pattern; provides counting and lookup utilities for card status tracking.
Primary Service Implementation
src/main/java/io/mosip/print/service/impl/PrintServiceImpl.java, src/main/java/io/mosip/print/service/PrintService.java
Refactored PrintService interface (removed generics, added generateCard method); implements credential decryption via PIN, biometric extraction, card attribute population, QR/photo encoding, PDF generation, digital signature application, and MSP card persistence; orchestrates credential-to-print-ready-card workflow.
Cryptographic Utilities
src/main/java/io/mosip/print/util/CryptoCoreUtil.java, CryptoUtil.java
Provides PKCS12 keystore-based RSA decryption (OAEP padding) with optional thumbprint handling; implements PBKDF2 key derivation and AES-GCM symmetric decryption for PIN-based encryption.
General Utilities
src/main/java/io/mosip/print/util/*.java (AuditLogRequestBuilder, CbeffToBiometricUtil, DateUtils, DigitalSignatureUtility, EmptyCheckUtils, Helpers, JsonUtil, PrintExceptionHandler, PrintInstializer, QrcodegeneratorUtils, RestApiClient, TemplateGenerator, TemplateManagerUtil, TokenHandlerUtil, Utilities, WebSubSubscriptionHelper)
Utilities for date/time arithmetic and parsing, JSON serialization, empty/null checks, file resource loading, digital signature generation, token validation, WebSub subscriptions, audit logging, and exception handling; switches logging from kernel to SLF4J/PrintLogger; updates property keys to print-specific namespace.
Configuration Properties
src/main/resources/application-local.properties, bootstrap.properties
Updates server port (8089), servlet path (/v1/print or /v1/cardprint), database credentials (mosip_print), token generation parameters, print service identifiers, partner/datashare settings, and removes legacy registration-processor configurations.
Model Classes
src/main/java/io/mosip/print/model/*.java (CertificateEntry, CompositeScore, KeyValuePair, MatchDecision, PrintcardMCE, QualityScore, Rectangle, Response, Score)
Introduces 9 model classes for certificate/private key pairs, biometric scoring, print card entities, PDF rectangle positioning, and generic response wrappers.
Test Classes & Resources
src/test/java/io/mosip/print/test/*.java, src/test/resources/*.json, .html
Adds 5 test classes (TestBootApplication, PrintControllerTest, PrintServiceTest, QrcodeGeneratorTest, UinCardGeneratorImplTest) and 3 test resources (RegistrationProcessorIdentity.json, csshtml.html, printTextFileJson.json) for integration and unit testing of controllers, services, and utilities.

Sequence Diagrams

sequenceDiagram
    participant Client
    participant Controller as Print Controller
    participant Service as PrintServiceImpl
    participant CryptoUtil as Crypto Utilities
    participant BioAPI as BioAPI/CBEFF
    participant PDFGen as PDF Generator
    participant Repository as DB Repository
    
    Client->>Controller: POST /callback/notifyPrint (EventModel)
    Controller->>Service: generateCard(eventModel)
    
    rect rgb(200, 220, 255)
        Note over Service,CryptoUtil: Credential Decryption Phase
        Service->>CryptoUtil: decrypt PIN-protected credential
        CryptoUtil-->>Service: decrypted JSON payload
    end
    
    rect rgb(220, 255, 220)
        Note over Service,BioAPI: Biometric Extraction Phase
        Service->>BioAPI: extractTemplate(BIR)
        BioAPI-->>Service: extracted biometric
        Service->>BioAPI: checkQuality(sample)
        BioAPI-->>Service: QualityScore
    end
    
    rect rgb(255, 240, 200)
        Note over Service,PDFGen: Card Generation Phase
        Service->>PDFGen: generate(template, cardData)
        PDFGen-->>Service: PDF bytes
        Service->>PDFGen: signAndEncryptPDF(pdf, cert, password)
        PDFGen-->>Service: signed PDF bytes
    end
    
    rect rgb(255, 220, 220)
        Note over Service,Repository: Persistence Phase
        Service->>Repository: save(MspCardEntity)
        Repository-->>Service: saved entity
    end
    
    Service-->>Controller: success
    Controller-->>Client: 200 OK
Loading
sequenceDiagram
    participant Service as PrintServiceImpl
    participant TemplateManager as Template Manager
    participant QRGen as QR Code Generator
    participant PDFGen as PDF Generator
    participant Crypto as CryptoCoreUtil
    
    rect rgb(240, 255, 240)
        Note over Service,TemplateManager: Template Rendering
        Service->>TemplateManager: merge(templateName, cardAttributes)
        TemplateManager-->>Service: rendered HTML bytes
    end
    
    rect rgb(255, 250, 220)
        Note over Service,QRGen: QR Code Generation
        Service->>QRGen: generateQrCode(cardData, QrVersion.V25)
        QRGen-->>Service: QR code PNG bytes
    end
    
    rect rgb(240, 220, 255)
        Note over Service,PDFGen: PDF Creation & Signing
        Service->>PDFGen: generate(templateHTML)
        PDFGen-->>Service: PDF OutputStream
        Service->>Crypto: loadP12() + getCertificate()
        Crypto-->>Service: PrivateKeyEntry + Certificate
        Service->>PDFGen: signAndEncryptPDF(pdf, cert, rect, password)
        PDFGen-->>Service: signed/encrypted PDF bytes
    end
    
    Service-->>Service: encodeQR & Photo to Base64
    Service-->>Service: populate MSP card attributes
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Rationale: This PR introduces a substantial, heterogeneous set of changes spanning new exception hierarchies, DTOs, service implementations, cryptographic utilities, configuration migration, and a complete service refactoring from kernel-based to print-module-based architecture. While many files follow repetitive patterns (constants, DTOs, exception types), the core service implementations (PrintServiceImpl, CbeffImpl, PDFGeneratorImpl, CryptoCoreUtil) contain dense logic with multiple integrations (encryption/decryption, PDF signing, biometric processing, template rendering). The dependency migration from kernel to print modules touches numerous files, and the new printService workflow orchestrates multiple sub-systems. The presence of dynamic encryption, certificate handling, and workflow state changes across decryption, biometric extraction, and PDF generation warrants careful verification of correctness and security.

Poem

🐰 A bundle of print arrives at last,
From kernel roots to futures fast,
With PDFs signed and QR codes bright,
Cards encrypted, biometrics tight!
Templates merge, and databases sing,
Spring Boot's grace brings everything. 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Mec dev' is vague and generic, failing to convey meaningful information about the changeset's primary purpose. Rename the title to clearly describe the main change, such as 'Refactor print service to decouple from kernel module' or 'Extract print module from registration processor with new services and utilities.'
Docstring Coverage ⚠️ Warning Docstring coverage is 68.35% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mec_dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
pom.xml (1)

130-167: Remove duplicate dependencies.

Both json-simple (lines 130-134, 160-163) and gson (lines 136-138, 165-167) are declared twice. Maven will use one declaration and ignore the other, but this creates confusion and maintenance overhead.

🔎 Proposed fix
 		</dependency>
-		<dependency>
-		    <groupId>com.googlecode.json-simple</groupId>
-		    <artifactId>json-simple</artifactId>
-		    <version>1.1.1</version>
-		</dependency>
-		<dependency>
-			<groupId>com.google.code.gson</groupId>
-			<artifactId>gson</artifactId>
-		</dependency>
 		<dependency>
 		    <groupId>org.json</groupId>
 		    <artifactId>json</artifactId>

Remove lines 160-167 to eliminate the duplicates.

src/main/java/io/mosip/print/service/PrintService.java (1)

17-19: Remove commented-out method signature.

The commented-out getDocuments method should be removed. Use version control to track historical changes.

🔎 Proposed fix
 	public void generateCard(EventModel eventModel) throws Exception;
-
-	// Map<String, byte[]> getDocuments(String credentialSubject, String sign,
-	// String cardType,
-	// boolean isPasswordProtected);
-	
 }
src/main/java/io/mosip/print/util/PrintInstializer.java (1)

67-71: Off-by-one error in retry loop.

Using i <= retryCount means if retryCount=3, there will be 4 attempts (i=0,1,2,3). This is likely unintended based on typical retry semantics.

🔎 Proposed fix
 private void retrySubscriptions() {
 	// Call Init Subscriptions for the count until no error in the subscription
-	for (int i = 0; i <= retryCount; i++) {
+	for (int i = 0; i < retryCount; i++) {
 		if (initSubsriptions()) {
 			return;
 		}
 	}
 }
src/main/java/io/mosip/print/service/impl/PrintServiceImpl.java (1)

308-378: Replace e.printStackTrace() with proper logging.

Multiple exception handlers use e.printStackTrace() (lines 309, 320, 330, 341, 351, 362, 372) before logging. This outputs to stderr and bypasses the configured logging framework. The logger is already being used on subsequent lines, so remove the printStackTrace() calls.

🔎 Proposed fix (example for one block)
 } catch (VidCreationException e) {
-	e.printStackTrace();
 	description.setMessage(PlatformErrorMessages.PRT_PRT_VID_CREATION_ERROR.getMessage());

Apply the same pattern to all other catch blocks.

🟠 Major comments (35)
src/main/java/io/mosip/print/constant/ApiName.java-97-97 (1)

97-97: Fix typo: DECRYPTPINBASSED should be DECRYPTPINBASED.

The constant name contains "BASSED" which appears to be a typo for "BASED". This should be corrected before merge, as renaming enum constants is a breaking change.

🔎 Proposed fix
-	DECRYPTPINBASSED, CREATEDATASHARE,
+	DECRYPTPINBASED, CREATEDATASHARE,
src/main/java/io/mosip/print/util/Helpers.java-19-22 (1)

19-22: Major: Null return and undocumented resource ownership.

This method has several concerns:

  1. Null return: Returns null when the resource is not found, requiring callers to perform null checks. This is error-prone and inconsistent with readFileFromResources, which throws an exception.
  2. Undocumented resource ownership: The returned InputStream must be closed by the caller, but this critical requirement is not documented. Missing cleanup will cause resource leaks.
🔎 Proposed fix for consistent error handling
-    public static InputStream readStreamFromResources(String filename) {
-        InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename);
-        return inputStream;
+    /**
+     * Reads a resource file and returns its InputStream.
+     * The caller is responsible for closing the returned InputStream.
+     *
+     * @param filename the resource filename
+     * @return the InputStream for the resource
+     * @throws IOException if the resource is not found
+     */
+    public static InputStream readStreamFromResources(String filename) throws IOException {
+        InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename);
+        if (inputStream == null) {
+            throw new IOException("Resource not found: " + filename);
+        }
+        return inputStream;
     }
src/main/java/io/mosip/print/dto/PrintMQDetails.java-7-9 (1)

7-9: Exclude password from toString() to prevent credential exposure.

The @Data annotation generates a toString() method that includes all fields, including the password field at line 17. This creates a security risk where credentials can be accidentally exposed through logging, debugging, or error messages.

🔎 Proposed fix to exclude password from toString()
+import lombok.ToString;
+
 @Data
 @NoArgsConstructor
 @AllArgsConstructor
+@ToString(exclude = "password")
 public class PrintMQDetails {
src/main/java/io/mosip/print/util/DateUtils.java-166-173 (1)

166-173: Wrong exception type being caught - java.lang.NullPointerException will not be caught.

The catch clause catches io.mosip.print.exception.NullPointerException (imported at line 35), but DateFormatUtils.format() throws java.lang.NullPointerException. The custom exception will never be thrown here, so null arguments will propagate as uncaught java.lang.NullPointerException.

This same issue exists in all formatDate and formatCalendar methods (lines 189-196, 213-221, 234-241, 255-262, 276-283, 298-306).

🔎 Proposed fix
 	public static String formatDate(final Date date, final String pattern) {
 		try {
 			return DateFormatUtils.format(date, pattern, null, null);
-		} catch (java.lang.IllegalArgumentException | NullPointerException e) {
+		} catch (java.lang.IllegalArgumentException | java.lang.NullPointerException e) {
 			throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
-					DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
+					DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getExceptionMessage(), e.getCause());
 		}
 	}
src/main/java/io/mosip/print/util/TokenHandlerUtil.java-52-53 (1)

52-53: Potential NullPointerException if clientId claim is missing.

If the token doesn't contain a clientId claim, claims.get("clientId") returns null, and calling .asString() will throw NPE. Add a null check.

🔎 Proposed fix
-			} else if (!claims.get("clientId").asString().equals(clientId)) {
+			Claim clientIdClaim = claims.get("clientId");
+			if (clientIdClaim == null || !clientId.equals(clientIdClaim.asString())) {
 				return false;
 			}
src/main/java/io/mosip/print/dto/CredentialRequestDto.java-25-25 (1)

25-25: Fix the spelling of "recepiant" to "recipient".

The field name recepiant is misspelled and should be recipient. Since this DTO is likely serialized and exposed via API contracts, fixing this typo now avoids introducing a breaking change later.

🔎 Proposed fix
-	private String recepiant;
+	private String recipient;
src/main/java/io/mosip/print/service/impl/UinCardGeneratorImpl.java-58-71 (1)

58-71: Update PrintServiceImpl to use the new configuration property key.

The migration from mosip.registration.processor.print.service.uincard.* to mosip.print.service.uincard.* is incomplete. PrintServiceImpl.java (line 109) still references the old property key mosip.registration.processor.print.service.uincard.password, which no longer exists in the configuration file. This will cause a NullPointerException at runtime when getPassword() is called (line 737). Update the constant to use the new key: mosip.print.service.uincard.password.

pom.xml-8-8 (1)

8-8: Downgrading Spring Boot from 2.0.9 to 2.0.2 introduces known security vulnerabilities.

Spring Boot 2.0.2 (May 2018) is vulnerable to CVE-2022-27772 (temporary-directory hijacking via AbstractConfigurableWebServerFactory.createTempDir), which affects all spring-boot versions prior to 2.2.11.RELEASE. Additionally, 2.0.9 contains security fixes that were released after 2.0.2. If 2.0.x must be used, upgrade to at least 2.0.9 or preferably 2.0.14 for all 2.0.x fixes. Consider upgrading to a supported Spring Boot version, as the 2.0 branch is end-of-life.

src/main/java/io/mosip/print/init/SetupPrint.java-26-26 (1)

26-26: Removal of default value creates a required property that may break deployments.

The default value of 60000 was removed from the @Value annotation. If mosip.event.delay is not configured in the environment, the application will fail to start with a property resolution error.

Consider either:

  1. Restoring the default value: @Value("${mosip.event.delay:60000}")
  2. Documenting this as a required property in deployment guides
🔎 Proposed fix to restore the default value
-	@Value("${mosip.event.delay}")
+	@Value("${mosip.event.delay:60000}")
 	private int taskSubsctiptionDelay;
src/main/java/io/mosip/print/exception/NullPointerException.java-12-37 (1)

12-37: Rename to avoid shadowing java.lang.NullPointerException.

This class name shadows the standard Java NullPointerException, which will cause confusion and potential bugs. Any code in the io.mosip.print.exception package (or that imports this class) that references NullPointerException will resolve to this custom exception instead of the JDK class, leading to unexpected behavior.

Consider renaming to NullArgumentException, InvalidArgumentException, or ArgumentNullException to clearly differentiate it from the standard Java exception.

🔎 Example rename to NullArgumentException
-public class NullPointerException extends BaseUncheckedException {
+public class NullArgumentException extends BaseUncheckedException {
 
 	/** Serializable version Id. */
 	private static final long serialVersionUID = 784321102100630614L;
 
 	/**
 	 * Constructor with errorCode, and rootCause
 	 * 
 	 * @param errorCode    The error code for this exception
 	 * @param errorMessage The error message for this exception
 	 */
-	public NullPointerException(String errorCode, String errorMessage) {
+	public NullArgumentException(String errorCode, String errorMessage) {
 		super(errorCode, errorMessage);
 	}
 
 	/**
 	 * @param arg0 Error Code Corresponds to Particular Exception
 	 * @param arg1 Message providing the specific context of the error.
 	 * @param arg2 Cause of exception
 	 */
-	public NullPointerException(String arg0, String arg1, Throwable arg2) {
+	public NullArgumentException(String arg0, String arg1, Throwable arg2) {
 		super(arg0, arg1, arg2);
 
 	}
 
 }
src/main/java/io/mosip/print/service/CbeffContainerI.java-16-20 (1)

16-20: Replace generic Exception with specific exception types.

The methods declare throws Exception, which is overly broad and forces callers to handle all exception types indiscriminately. Consider defining specific checked exceptions (e.g., CbeffException or BIRProcessingException) to provide clearer error semantics and enable more precise exception handling.

💡 Recommended approach

Define a specific exception type and update the method signatures:

public abstract U createBIRType(List<T> bir) throws CbeffException;

public abstract U updateBIRType(List<T> bir, byte[] fileBytes) throws CbeffException;

public abstract boolean validateXML(byte[] fileBytes, byte[] xsdBytes) throws CbeffException;
src/main/java/io/mosip/print/exception/IllegalArgumentException.java-13-13 (1)

13-13: Rename to avoid name collision with java.lang.IllegalArgumentException.

This class name shadows java.lang.IllegalArgumentException, which can cause:

  • Import ambiguity requiring fully-qualified names
  • Unintended exception type usage
  • Confusion for developers maintaining the code
  • Potential for catching the wrong exception type

Rename to a domain-specific name like InvalidArgumentException, PrintIllegalArgumentException, or MosipIllegalArgumentException.

🔎 Proposed fix
-public class IllegalArgumentException extends BaseUncheckedException {
+public class InvalidArgumentException extends BaseUncheckedException {
 	/** Serializable version Id. */
 	private static final long serialVersionUID = 924722202110630628L;
 
-	public IllegalArgumentException(String errorCode, String errorMessage) {
+	public InvalidArgumentException(String errorCode, String errorMessage) {
 		super(errorCode, errorMessage);
 
 	}
 
-	public IllegalArgumentException(String errorCode, String errorMessage, Throwable cause) {
+	public InvalidArgumentException(String errorCode, String errorMessage, Throwable cause) {
 		super(errorCode, errorMessage, cause);
 
 	}
 
 }
src/main/java/io/mosip/print/exception/InfoItem.java-23-29 (1)

23-29: Remove redundant Lombok annotations or make fields private.

The fields are declared public while also annotated with @Getter and @Setter. Public fields are directly accessible, making the Lombok-generated accessors redundant. For proper encapsulation, make the fields private and retain the Lombok annotations.

🔎 Proposed fix
 	@Getter
 	@Setter
-	public String errorCode = null;
+	private String errorCode = null;
 
 	@Getter
 	@Setter
-	public String errorText = null;
+	private String errorText = null;
src/test/java/io/mosip/print/test/controller/PrintControllerTest.java-56-62 (1)

56-62: Uncomment and properly configure the mock to validate service behavior.

The mock setup on line 58 is commented out, which means this test only verifies that the endpoint returns 200 OK but doesn't validate that the PrintService.generateCard() method is actually called or that it processes the request correctly. This significantly reduces the test's effectiveness.

🔎 Proposed fix
 	@Test
 	public void testHandleSubscribeEventSuccess() throws Exception {
 		byte[] pdfbytes = "pdf".getBytes();
-//		Mockito.when(printService.generateCard(Mockito.any())).thenReturn(pdfbytes);
+		Mockito.when(printService.generateCard(Mockito.any())).thenReturn(pdfbytes);
 		mockMvc.perform(MockMvcRequestBuilders.post("/print/callback/notifyPrint")
 				.contentType(MediaType.APPLICATION_JSON_VALUE).content(reqCredentialEventJson.getBytes()))
 				.andExpect(status().isOk());
+		Mockito.verify(printService, Mockito.times(1)).generateCard(Mockito.any());
 	}
src/main/java/io/mosip/print/service/PrintDBService.java-25-39 (1)

25-39: Remove commented-out code.

Large blocks of commented-out code (lines 25-39) reduce readability and should be removed. Use version control to track historical changes instead.

🔎 Proposed fix
 		PrintcardMCE card = new PrintcardMCE();
 		card.setId(obj.getId());
 		card.setName(obj.getName());
-//            card.setProvince(obj.getProvince());
-//            card.setCity(obj.getCity());
-//            card.setZip(obj.getZip());
-//            card.setZone(obj.getZone());
 		card.setRequestId(obj.getRequestId());
-//            card.setRequestId1(obj.getRequestId1());
-//            card.setDownloadDate(new Date());
-//            card.setRegistrationDate(obj.getRegistrationDate());
-//            card.setRegistrationCenterId(obj.getRegistrationCenterId());
-//            card.setResident(obj.getResident());
-//            card.setIntroducer(obj.getIntroducer());
-//            card.setRid(obj.getRid());
-
-//        card.setId(obj.getId());
-//            card.setBirthdate(obj.getBirthdate());
 		entityManager.persist(card);
src/main/java/io/mosip/print/service/PrintDBService.java-21-21 (1)

21-21: Remove unused variable.

The taskStart variable is assigned but never used, suggesting incomplete timing/logging logic.

🔎 Proposed fix
 	try {
-		long taskStart = System.currentTimeMillis();
 		PrintcardMCE card = new PrintcardMCE();
src/main/java/io/mosip/print/service/PrintDBService.java-50-52 (1)

50-52: Remove commented-out method signature.

The commented-out method signature should be removed to improve code clarity.

🔎 Proposed fix
 	}
-
-	// Map<String, byte[]> getDocuments(String credentialSubject, String sign,
-	// String cardType,
-	// boolean isPasswordProtected);
-
 }
src/main/java/io/mosip/print/util/CryptoUtil.java-167-223 (1)

167-223: Remove large block of commented-out code.

This commented-out PDF signing implementation adds significant noise. If this code is intended for future use, track it in version control or a separate branch instead.

src/main/java/io/mosip/print/util/CryptoUtil.java-50-59 (1)

50-59: Remove commented-out debug code.

This commented-out main() method with hardcoded test data should be removed. It adds noise and could accidentally expose test credentials if uncommented.

🔎 Proposed fix
-	/*
-	 * public static void main(String[] args) throws NoSuchAlgorithmException,
-	 * InvalidKeySpecException, InvalidKeyException,
-	 * InvalidAlgorithmParameterException, IllegalBlockSizeException,
-	 * BadPaddingException { CryptoUtil cu = new CryptoUtil();
-	 * CryptoWithPinRequestDto cp = new CryptoWithPinRequestDto(); cp.setData(
-	 * "sH0LIHnUee6wGni1stdDrO2GS_MzjwchlO6-0rmWbrduPwO4BE95xEyCDnRzy3joKz_l4UNRa64t15jWcT3dW2vI3w"
-	 * ); cp.setUserPin("abc123"); CryptoWithPinResponseDto result =
-	 * cu.decryptWithPin(cp); System.out.println("result: " + result); }
-	 */
src/main/java/io/mosip/print/exception/PlatformErrorMessages.java-69-72 (1)

69-72: Duplicate error code "026" used for two different errors.

Both API_NOT_ACCESSIBLE_EXCEPTION and CERTIFICATE_THUMBPRINT_ERROR use error code PRT_PRINT_PREFIX + "026". This will cause confusion when debugging issues. Assign a unique code to CERTIFICATE_THUMBPRINT_ERROR.

🔎 Proposed fix
 DATASHARE_EXCEPTION(PlatformConstants.PRT_PRINT_PREFIX + "025", "Data share api failure"),
 API_NOT_ACCESSIBLE_EXCEPTION(PlatformConstants.PRT_PRINT_PREFIX + "026", "Api not accessible failure"),
-CERTIFICATE_THUMBPRINT_ERROR(PlatformConstants.PRT_PRINT_PREFIX + "026", "certificate thumbprint failure"),
+CERTIFICATE_THUMBPRINT_ERROR(PlatformConstants.PRT_PRINT_PREFIX + "028", "certificate thumbprint failure"),
 PRT_INVALID_KEY_EXCEPTION(PlatformConstants.PRT_PRINT_PREFIX + "027", "invalid key");
src/main/java/io/mosip/print/util/DigitalSignatureUtility.java-64-76 (1)

64-76: Potential NullPointerException after logging errors.

When response.getErrors() is non-empty, errors are logged but execution continues to line 71 where response.getResponse() may be null, causing an NPE at signResponseDto.getSignature(). Consider throwing an exception when errors are present.

🔎 Proposed fix
 if (response.getErrors() != null && response.getErrors().size() > 0) {
     response.getErrors().stream().forEach(r -> {
         printLogger.error(LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.UIN.toString(), "",
                 "DigitalSignatureUtility::getDigitalSignature():: error with error message " + r.getMessage());
     });
+    throw new DigitalSignatureException("Digital signature API returned errors: " + 
+            response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(", ")));
 }

+if (response.getResponse() == null) {
+    throw new DigitalSignatureException("Digital signature API returned null response");
+}
+
 SignResponseDto signResponseDto = mapper.readValue(mapper.writeValueAsString(response.getResponse()), SignResponseDto.class);
src/main/java/io/mosip/print/util/CryptoUtil.java-18-18 (1)

18-18: DatatypeConverter is deprecated since Java 9 and removed from the JDK in Java 11.

While the project explicitly includes JAXB dependencies, use Apache Commons Codec's Hex class instead, which is already a project dependency. Replace with org.apache.commons.codec.binary.Hex.encodeHexString() for hex encoding/decoding to avoid reliance on the deprecated JAXB module.

src/main/java/io/mosip/print/service/impl/QrcodeGeneratorImpl.java-81-81 (1)

81-81: Verify binary string parsing handles edge cases correctly.

The regex "(?<=\\G.{8})" splits the binary string into 8-bit chunks, but there's no validation that:

  1. The input contains only '0' and '1' characters
  2. The length is a multiple of 8

Invalid input could produce incorrect characters or throw uncaught NumberFormatException.

🔎 Proposed fix: Add validation
 	@Override
 	public byte[] generateQrCodeFromBinaryData(String data, QrVersion version)
 			throws QrcodeGenerationException, IOException {
 		QrcodegeneratorUtils.verifyInput(data, version);
+		if (!data.matches("[01]+")) {
+			throw new QrcodeGenerationException(
+				QrcodeExceptionConstants.INVALID_INPUT_DATA_EMPTY.getErrorCode(),
+				"Binary data must contain only 0 and 1 characters",
+				null);
+		}
+		if (data.length() % 8 != 0) {
+			throw new QrcodeGenerationException(
+				QrcodeExceptionConstants.INVALID_INPUT_DATA_EMPTY.getErrorCode(),
+				"Binary data length must be a multiple of 8",
+				null);
+		}
 		StringBuilder stringBuilder = new StringBuilder();
 		Arrays.stream(data.split("(?<=\\G.{8})")).forEach(s -> stringBuilder.append((char) Integer.parseInt(s, 2)));
 		return generateQrCode(stringBuilder.toString(), version);
 	}
src/main/java/io/mosip/print/util/WebSubSubscriptionHelper.java-39-39 (1)

39-39: Type safety weakened: PublisherClient now accepts Object.

The generic type parameter is declared as Object instead of CredentialStatusEvent, removing compile-time type safety. Currently, only CredentialStatusEvent is published via pb.publishUpdate(), making this over-generalization unnecessary and allowing arbitrary objects to be published at runtime.

If multiple event types need to be supported in the future, consider:

  1. Using a base interface/class for all event types
  2. Creating separate publisher clients for different event types
  3. Using bounded wildcards if flexibility is required

Otherwise, revert the generic parameter to CredentialStatusEvent to restore type safety.

src/main/java/io/mosip/print/service/impl/TemplateManagerBuilderImpl.java-17-36 (1)

17-36: Singleton @Component with mutable state causes shared state pollution.

The class is marked as @Component (default singleton scope) but the fluent builder methods mutate instance fields. Concurrent or repeated use of the same injected builder will share/overwrite state unexpectedly.

Either:

  1. Use @Scope("prototype") to get a new instance per injection, or
  2. Have fluent methods return a new builder instance (immutable builder), or
  3. Remove @Component and instantiate manually where needed.
🔎 Proposed fix (Option 1: Prototype scope)
+import org.springframework.context.annotation.Scope;
+
 @Getter
 @Component
+@Scope("prototype")
 public class TemplateManagerBuilderImpl implements TemplateManagerBuilder {
src/main/java/io/mosip/print/service/impl/CbeffContainerImpl.java-22-43 (1)

22-43: Instance field birType is not thread-safe.

The birType field is modified by createBIRType via load() and used across method calls. If this class is used concurrently, this could lead to race conditions. Consider making birType a local variable within createBIRType.

🔎 Proposed fix
-public class CbeffContainerImpl extends CbeffContainerI<BIR, BIRType> {
-
-	private BIRType birType;
+public class CbeffContainerImpl extends CbeffContainerI<BIR, BIRType> {

 	@Override
 	public BIRType createBIRType(List<BIR> birList) {
-		load();
+		BIRType birType = createBaseBIRType();
 		List<BIRType> birTypeList = new ArrayList<>();
 		if (birList != null && birList.size() > 0) {
 			for (BIR bir : birList) {
 				birTypeList.add(bir.toBIRType(bir));
 			}
 		}
 		birType.setBir(birTypeList);
 		return birType;
 	}

-	private void load() {
+	private BIRType createBaseBIRType() {
+		BIRType birType = new BIRType();
-		birType = new BIRType();
 		BIRInfoType birInfo = new BIRInfoType();
 		birInfo.setIntegrity(false);
 		birType.setBIRInfo(birInfo);
+		return birType;
 	}

Committable suggestion skipped: line range outside the PR's diff.

src/main/java/io/mosip/print/service/impl/BioApiImpl.java-86-97 (1)

86-97: Potential NullPointerException due to array gaps.

When max.isPresent() returns false, count is not incremented but the loop continues, leaving null elements in matchingScore. The subsequent Arrays.stream(matchingScore).mapToDouble(Score::getInternalScore) on line 97 will throw an NPE when accessing a null element.

🔎 Proposed fix
 	public CompositeScore compositeMatch(BIR[] sampleList, BIR[] recordList, KeyValuePair[] flags) {
-		Score matchingScore[] = new Score[sampleList.length];
-		int count = 0;
+		List<Score> matchingScores = new ArrayList<>();
 		for (BIR sampleValue : sampleList) {
 			Score[] match = match(sampleValue, recordList, flags);
 			Optional<Score> max = Arrays.stream(match).max(Comparator.comparing(Score::getInternalScore));
-			if (max.isPresent()) {
-				matchingScore[count] = max.get();
-				count++;
-			}
+			max.ifPresent(matchingScores::add);
 		}
+		Score[] matchingScore = matchingScores.toArray(new Score[0]);
+		if (matchingScore.length == 0) {
+			CompositeScore compositeScore = new CompositeScore();
+			compositeScore.setIndividualScores(matchingScore);
+			compositeScore.setInternalScore(0L);
+			compositeScore.setScaledScore(0L);
+			return compositeScore;
+		}
 		double sum = Arrays.stream(matchingScore).mapToDouble(Score::getInternalScore).sum();
 		CompositeScore compositeScore = new CompositeScore();
 		compositeScore.setIndividualScores(matchingScore);

Also add import:

import java.util.ArrayList;
import java.util.List;
src/main/java/io/mosip/print/service/impl/TemplateManagerImpl.java-77-77 (1)

77-77: Returning null on failure allows NullPointerException to occur in callers.

The merge(InputStream, Map) method returns null when context is null (line 60) or when the merge operation fails (line 62). The caller in TemplateGenerator.getTemplate() assigns this result directly to fileTextStream without null validation (line 106) and returns it unconditionally (line 110), violating the interface contract that specifies returning "merged template content as InputStream". Either throw an exception for failure cases or return an empty InputStream.

src/main/java/io/mosip/print/util/CryptoCoreUtil.java-183-200 (1)

183-200: Silent exception swallowing in symmetricDecrypt.

The catch block at lines 196-198 silently swallows exceptions and returns null, which will cause NPE or unexpected behavior upstream. This makes debugging cryptographic failures extremely difficult.

🔎 Proposed fix
 private static byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] aad) {
 	byte[] output = null;
 	try {
 		Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
 		byte[] randomIV = Arrays.copyOfRange(data, data.length - cipher.getBlockSize(), data.length);
 		SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
 		GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, randomIV);

 		cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
 		if (aad != null && aad.length != 0) {
 			cipher.updateAAD(aad);
 		}
 		output = cipher.doFinal(Arrays.copyOf(data, data.length - cipher.getBlockSize()));
 	} catch (Exception e) {
-
+		throw new CryptoManagerException("Symmetric decryption failed", e.getMessage(), e);
 	}
 	return output;
 }

Committable suggestion skipped: line range outside the PR's diff.

src/main/java/io/mosip/print/util/CryptoCoreUtil.java-108-111 (1)

108-111: Avoid e.printStackTrace() - use proper logging.

Using printStackTrace() writes to stderr and is not captured by logging frameworks. Log the exception properly and don't throw a generic exception with a vague message.

🔎 Proposed fix
+	private static final Logger logger = LoggerFactory.getLogger(CryptoCoreUtil.class);
+
 	// In decryptData method:
 		} catch (Exception e) {
-			e.printStackTrace();
+			logger.error("Decryption failed", e);
+			throw new CryptoManagerException("Decryption failed", e.getMessage(), e);
 		}
-		throw new Exception("Not able to decrypt the data.");

Committable suggestion skipped: line range outside the PR's diff.

src/test/java/io/mosip/print/test/util/UinCardGeneratorImplTest.java-69-101 (1)

69-101: Multiple issues: resource leaks and incorrect mock setup.

  1. FileInputStream at line 72 is never closed and is consumed before being passed to the mock (line 81).
  2. buffer.toString() (line 84) produces [B@hashcode instead of actual data - use new String(buffer) or Base64 encoding.
  3. The stream is consumed in the while loop before being used in the mock, so pdfGenerator.generate(is) will receive an exhausted stream.
🔎 Proposed fix
 @Test
 public void testCardGenerationSuccess() throws IOException, ApisResourceAccessException {
 	ClassLoader classLoader = getClass().getClassLoader();
 	String inputFile = classLoader.getResource("csshtml.html").getFile();
-	InputStream is = new FileInputStream(inputFile);
-
-	byte[] buffer = new byte[8192];
-	int bytesRead;
-	ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-	while ((bytesRead = is.read(buffer)) != -1) {
-		outputStream.write(buffer, 0, bytesRead);
-	}
+	byte[] fileContent;
+	try (InputStream is = new FileInputStream(inputFile)) {
+		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+		byte[] buffer = new byte[8192];
+		int bytesRead;
+		while ((bytesRead = is.read(buffer)) != -1) {
+			outputStream.write(buffer, 0, bytesRead);
+		}
+		fileContent = outputStream.toByteArray();
+	}
 
-	Mockito.when(pdfGenerator.generate(is)).thenReturn(outputStream);
+	ByteArrayOutputStream pdfOutput = new ByteArrayOutputStream();
+	pdfOutput.write(fileContent);
+	Mockito.when(pdfGenerator.generate(any(InputStream.class))).thenReturn(pdfOutput);
 	ResponseWrapper<SignatureResponseDto> responseWrapper = new ResponseWrapper<>();
 	SignatureResponseDto signatureResponseDto = new SignatureResponseDto();
-	signatureResponseDto.setData(buffer.toString());
+	signatureResponseDto.setData(java.util.Base64.getEncoder().encodeToString(fileContent));
 	responseWrapper.setResponse(signatureResponseDto);

Committable suggestion skipped: line range outside the PR's diff.

src/main/java/io/mosip/print/entity/MspCardEntity.java-23-29 (1)

23-29: Remove @Component from JPA entity and add serialVersionUID.

The @Component annotation is inappropriate for a JPA entity. Entities are managed by the persistence context, not as Spring beans. Additionally, Serializable requires a serialVersionUID constant.

Note: There is a PrintcardMCE entity in src/main/java/io/mosip/print/model/PrintcardMCE.java also mapped to msp_card. While MspCardEntity specifies schema as "print" and PrintcardMCE does not, having two entity classes for the same logical table introduces unnecessary complexity and field inconsistencies (e.g., jsonData vs name, type differences for downloadDate and registrationDate). Consider consolidating them into a single entity.

src/main/java/io/mosip/print/service/impl/CbeffImpl.java-195-206 (1)

195-206: Remove commented-out test code before merging.

This commented-out main method with a hardcoded local file path (C:\\Users\\M1046464\\...) should be removed. Debug/test artifacts shouldn't be committed.

🔎 Proposed fix
-	//TODO for testing, will be removed later
-//	public static void main(String arg[]) throws Exception
-//	{
-//		Map<String,String> test= new CbeffImpl().getBDBBasedOnType(readCreatedXML(),"Iris",null);
-//		System.out.println(test.size());
-//		
-//	}
-//	
-//	private static byte[] readCreatedXML() throws IOException {
-//		byte[] fileContent = Files.readAllBytes(Paths.get("C:\\Users\\M1046464\\Downloads\\cbeff1.xml"));
-//		return fileContent;
-//	}
 }
src/main/java/io/mosip/print/service/impl/CbeffImpl.java-52-57 (1)

52-57: loadXSD may block indefinitely and lacks error handling.

The URL connection has no timeout configured. If the config server is unresponsive, this @PostConstruct method will block application startup indefinitely. Consider adding connection/read timeouts and logging the failure.

🔎 Proposed fix with timeout and error handling
 @PostConstruct
 public void loadXSD() throws IOException {
-	try (InputStream xsdBytes = new URL(configServerFileStorageURL + schemaName).openStream()) {
+	URL url = new URL(configServerFileStorageURL + schemaName);
+	java.net.URLConnection connection = url.openConnection();
+	connection.setConnectTimeout(10000); // 10 seconds
+	connection.setReadTimeout(30000);    // 30 seconds
+	try (InputStream xsdBytes = connection.getInputStream()) {
 		xsd = IOUtils.toByteArray(xsdBytes);
+	} catch (IOException e) {
+		// Log and rethrow or handle gracefully
+		throw new IOException("Failed to load XSD from " + url, e);
 	}
 }
src/main/java/io/mosip/print/service/impl/PrintServiceImpl.java-236-241 (1)

236-241: getSignature may throw ArrayIndexOutOfBoundsException if input format is unexpected.

The method splits on "." and accesses indices [0] and [2] without validating the array length. If the signature doesn't contain at least 3 parts, this will throw an exception.

🔎 Proposed fix with validation
 private String getSignature(String sign, String crdential) {
+	if (sign == null) {
+		throw new IllegalArgumentException("Signature cannot be null");
+	}
 	String[] parts = sign.split("\\.");
+	if (parts.length < 3) {
+		throw new IllegalArgumentException("Invalid signature format: expected at least 3 parts separated by '.'");
+	}
-	String signHeader = sign.split("\\.")[0];
-	String signData = sign.split("\\.")[2];
+	String signHeader = parts[0];
+	String signData = parts[2];
 	String signature = signHeader + "." + crdential + "." + signData;
 	return signature;
 }
🟡 Minor comments (30)
src/main/java/io/mosip/print/constant/DateUtilConstants.java-28-30 (1)

28-30: Fix typo in getter method name.

getEexceptionMessage has a duplicate 'e' and should be getExceptionMessage. This typo will propagate to all callers and create an inconsistent API.

🔎 Proposed fix
-	public String getEexceptionMessage() {
+	public String getExceptionMessage() {
 		return exceptionMessage;
 	}
src/main/java/io/mosip/print/util/DateUtils.java-78-79 (1)

78-79: Javadoc references non-existent package io.mosip.kernel.core.exception.

Multiple @throws clauses reference io.mosip.kernel.core.exception.IllegalArgumentException and io.mosip.kernel.core.exception.ParseException, but this module uses io.mosip.print.exception. Update the Javadoc to reference the correct package.

Also applies to: 99-100, 120-121, 141-142, 162-164, 185-187, 209-211, 230-232, 251-253, 272-274, 294-296, 320-326, 349-355, 379-385, 409-415, 441-447, 471-477, 501-507, 531-537, 710-714, 753-760, 784-788, 813-817, 840-845

src/main/java/io/mosip/print/constant/DateUtilConstants.java-6-6 (1)

6-6: Fix spelling error in message string.

"occours" should be "occurs".

🔎 Proposed fix
-	PARSE_EXCEPTION_ERROR_CODE("KER-UTL-103", "Parsing error occours");
+	PARSE_EXCEPTION_ERROR_CODE("KER-UTL-103", "Parsing error occurs");
src/main/java/io/mosip/print/util/DateUtils.java-866-870 (1)

866-870: Passing e.getCause() loses the original exception context.

When wrapping exceptions, passing e.getCause() instead of e loses the immediate exception's message and stack trace. This makes debugging harder. Use e directly as the cause.

🔎 Proposed fix
 		} catch (Exception e) {
 			throw new io.mosip.print.exception.ParseException(
 					DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getErrorCode(),
-					DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getEexceptionMessage(), e.getCause());
+					DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getExceptionMessage(), e);
 		}
src/main/java/io/mosip/print/util/TokenHandlerUtil.java-58-62 (1)

58-62: Potential NullPointerException in logging and consider using parameterized logging.

e.getMessage() can return null, causing .concat() to throw NPE. Also, string concatenation in log statements is inefficient. Use parameterized logging instead.

🔎 Proposed fix
 		} catch (JWTDecodeException e) {
-			LOGGER.error("JWT DECODE EXCEPTION ::" .concat(e.getMessage()).concat(ExceptionUtils.getStackTrace(e)));
+			LOGGER.error("JWT DECODE EXCEPTION :: {}", e.getMessage(), e);
 			return false;
 		} catch (Exception e) {
-			LOGGER.error(e.getMessage().concat(ExceptionUtils.getStackTrace(e)));
+			LOGGER.error("Token validation failed: {}", e.getMessage(), e);
 			return false;
 		}
src/main/java/io/mosip/print/dto/CryptoWithPinRequestDto.java-1-6 (1)

1-6: Remove or populate the empty comment block.

The file header contains an empty comment block. Either remove it or add appropriate copyright/license information.

src/main/java/io/mosip/print/service/impl/PrintImpl.java-16-22 (1)

16-22: Remove trailing empty lines.

Multiple blank lines at the end of the file are unnecessary and should be removed to maintain code cleanliness.

.github/workflows/manual_push_trigger.yml-15-15 (1)

15-15: Update GitHub Actions to current versions.

The actions actions/checkout@v2, actions/setup-java@v1, and actions/cache@v1 use deprecated runners that may not execute on current GitHub Actions infrastructure.

🔎 Proposed fix
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v4
     - name: Set up JDK 11
-      uses: actions/setup-java@v1
+      uses: actions/setup-java@v4
       with:
         ref: ${{ github.ref }}
         java-version: 11
         server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
         settings-path: ${{ github.workspace }} # location for the settings.xml file
     - name: Setup branch and env
       run: |
         # Strip git ref prefix from version
         echo "BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,')" >> $GITHUB_ENV
         echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
 
     - name: Setup branch and GPG public key 
       run: |
         # Strip git ref prefix from version
         echo ${{ env.BRANCH_NAME }}
         echo ${{ env.GPG_TTY }}
         sudo apt-get --yes install gnupg2
 #        gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg
 #        gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}}  --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg
      
-    - uses: actions/cache@v1
+    - uses: actions/cache@v4
       with:
         path: ~/.m2/repository

Based on learnings, GitHub Actions should use up-to-date action versions to ensure compatibility with current runners.

Also applies to: 17-17, 38-38

src/main/java/io/mosip/print/service/impl/UinCardGeneratorImpl.java-134-140 (1)

134-140: Replace printStackTrace() with logger.

Calling e.printStackTrace() on Line 135 bypasses the logging framework and writes directly to stderr, which is not captured by centralized logging systems. This is inconsistent with the structured logging approach used elsewhere in this class.

🔎 Proposed fix
 } 
-	  catch (ApisResourceAccessException e) {
-			e.printStackTrace();
-			printLogger.error(LoggerFileConstant.SESSIONID.toString(),
+	  catch (ApisResourceAccessException e) {
+			printLogger.error(LoggerFileConstant.SESSIONID.toString(),
 LoggerFileConstant.REGISTRATIONID.toString(), "",
 			PlatformErrorMessages.PRT_PRT_PDF_SIGNATURE_EXCEPTION.name() + e.getMessage()
 + ExceptionUtils.getStackTrace(e)); throw new PDFSignatureException(
 e.getMessage() + ExceptionUtils.getStackTrace(e)); }
src/main/java/io/mosip/print/model/MatchDecision.java-5-7 (1)

5-7: Fix the incorrect Javadoc comment.

The Javadoc comment says "The Class Score" but the actual class name is MatchDecision. This appears to be a copy-paste error.

🔎 Proposed fix
 /**
- * The Class Score.
+ * The Class MatchDecision.
  * 
  * @author Manoj SP
  */
src/main/java/io/mosip/print/dto/BaseResponseDTO.java-29-35 (1)

29-35: Incomplete null handling in setErrors method.

When errorsList is null, the errors field is not updated and retains its previous value. This could lead to stale error data remaining in the response object when the intent is to clear errors.

🔎 Proposed fix
 public void setErrors(List<Errors> errorsList) {
     if (errorsList != null) {
         errors = new Errors[errorsList.size()];
         for (int i = 0; i < errorsList.size(); i++)
             errors[i] = errorsList.get(i);
+    } else {
+        errors = null;
     }
 }
src/main/java/io/mosip/print/exception/PDFGeneratorException.java-15-21 (1)

15-21: Fix Javadoc inconsistency.

The Javadoc references "PDFGeneratorGenericException" but the actual class name is "PDFGeneratorException". Update the documentation to match the class name.

🔎 Proposed fix
 /**
- * Constructor for PDFGeneratorGenericException
+ * Constructor for PDFGeneratorException
  * 
  * @param errorCode    The errorcode
  * @param errorMessage The errormessage
  * @param cause        The cause
  */
 /**
- * Constructor for PDFGeneratorGenericException
+ * Constructor for PDFGeneratorException
  * 
  * @param errorCode    The errorcode
  * @param errorMessage The errormessage
  */

Also applies to: 26-31

src/main/java/io/mosip/print/constant/TemplateManagerConstant.java-13-13 (1)

13-13: Fix typo in enum constant name.

The constant TEMPATE_NAME_NULL has a typo—"TEMPATE" should be "TEMPLATE" for consistency with the other constants and the error message.

🔎 Proposed fix
 	WRITER_NULL("Writer cannot be null"), TEMPLATE_VALUES_NULL("Values cannot be null, it requires process template"),
 	TEMPLATE_INPUT_STREAM_NULL("Template cannot be null"), ENCODING_TYPE_NULL("Encoding type cannot be null"),
-	TEMPATE_NAME_NULL("Template name cannot be null");
+	TEMPLATE_NAME_NULL("Template name cannot be null");
src/main/java/io/mosip/print/constant/HubMode.java-39-41 (1)

39-41: Fix getter method naming to follow JavaBeans conventions.

The getter method name gethubModeValue() violates Java naming conventions. The 'M' in 'Mode' should be capitalized.

🔎 Proposed fix
-	public String gethubModeValue() {
+	public String getHubModeValue() {
 		return this.hubModeValue;
 	}
src/test/java/io/mosip/print/test/controller/PrintControllerTest.java-49-49 (1)

49-49: Replace deprecated MockitoAnnotations.initMocks() API.

MockitoAnnotations.initMocks(this) is deprecated. Use MockitoAnnotations.openMocks(this) instead, which returns an AutoCloseable that should be closed after tests.

🔎 Proposed fix
+	private AutoCloseable closeable;
+	
 	@Before
 	public void setup() throws Exception {
-		MockitoAnnotations.initMocks(this);
+		closeable = MockitoAnnotations.openMocks(this);
 		this.mockMvc = MockMvcBuilders.standaloneSetup(printController).build();
 		EventModel credentialEvent = new EventModel();
 		reqCredentialEventJson = gson.toJson(credentialEvent);
 	}

Add an @After method to close the resources:

@After
public void tearDown() throws Exception {
	if (closeable != null) {
		closeable.close();
	}
}
src/main/java/io/mosip/print/constant/TemplateManagerExceptionCodeConstant.java-12-12 (1)

12-12: Fix typo in user-facing error message.

"occured" should be spelled "occurred".

🔎 Proposed fix
-	TEMPLATE_PARSING("KER-TEM-003", "Exception occured during template processing"),
+	TEMPLATE_PARSING("KER-TEM-003", "Exception occurred during template processing"),
src/main/java/io/mosip/print/exception/CryptoManagerException.java-30-38 (1)

30-38: Fix misleading Javadoc and remove unused parameter.

The Javadoc refers to "access denied exception" but this is CryptoManagerException. Additionally, the message1 parameter is unused.

🔎 Proposed fix
 	/**
-	 * Instantiates a new access denied exception.
+	 * Instantiates a new crypto manager exception.
 	 *
 	 * @param message the message
 	 * @param cause   the cause
 	 */
-	public CryptoManagerException(String message, String message1, Throwable cause) {
+	public CryptoManagerException(String message, Throwable cause) {
 		super(PlatformErrorMessages.PRT_AUT_ACCESS_DENIED.getCode(), message, cause);
 	}
src/main/java/io/mosip/print/exception/CryptoManagerException.java-26-28 (1)

26-28: Remove unused parameter message1.

The message1 parameter is declared but never used in the constructor body. This creates confusion and should either be used or removed.

🔎 Proposed fix
-	public CryptoManagerException(String message, String message1) {
+	public CryptoManagerException(String message) {
 		super(PlatformErrorMessages.PRT_AUT_ACCESS_DENIED.getCode(), message);
 	}
src/main/java/io/mosip/print/util/Utilities.java-4-7 (1)

4-7: Remove unused imports.

The imports InputStream, MalformedURLException, URL, StandardCharsets, and IOUtils are not used in the code and should be removed.

src/test/java/io/mosip/print/test/service/impl/PrintServiceTest.java-118-125 (1)

118-125: Test is ignored, providing no coverage.

The only test method testQrcodegeneration() is annotated with @Ignore, meaning no tests will actually execute. This defeats the purpose of having test scaffolding. Either implement the test properly or add a TODO comment explaining when it will be enabled.

src/main/java/io/mosip/print/constant/PDFGeneratorExceptionCodeConstant.java-26-35 (1)

26-35: Fix copy-pasted Javadoc.

The constructor Javadoc references UINErrorConstants Enum (line 27), but this is PDFGeneratorExceptionCodeConstant.

🔎 Proposed fix
 	/**
-	 * Constructor for UINErrorConstants Enum.
+	 * Constructor for PDFGeneratorExceptionCodeConstant Enum.
 	 * 
 	 * @param errorCode    the error code.
 	 * @param errorMessage the error message.
 	 */
src/main/java/io/mosip/print/constant/WebSubClientErrorCode.java-65-65 (1)

65-65: Fix typo in Javadoc.

The Javadoc has an extra character: @return {@link #errorMessage}r should be @return {@link #errorMessage}.

🔎 Proposed fix
 	/**
 	 * Function to get the error message
 	 * 
-	 * @return {@link #errorMessage}r
+	 * @return {@link #errorMessage}
 	 */
 	public String getErrorMessage() {
src/main/java/io/mosip/print/constant/WebSubClientErrorCode.java-21-25 (1)

21-25: Fix typos in enum constant names and messages.

Three issues:

  1. Lines 21, 23, 25: AUTHENTTICATED should be AUTHENTICATED (remove extra 'T')
  2. Line 25: "maching" should be "matching"

These typos reduce code clarity and may hinder searchability.

🔎 Proposed fix
-	AUTHENTTICATED_CONTENT_VERIFICATION_HEADER_ERROR("KER-WSC-106", "Error occured while verifing authenticated content :- header for signature is empty or null"),
+	AUTHENTICATED_CONTENT_VERIFICATION_HEADER_ERROR("KER-WSC-106", "Error occured while verifying authenticated content :- header for signature is empty or null"),
 	
-	AUTHENTTICATED_CONTENT_ANNOTATION_SECRET_ERROR("KER-WSC-107", "Error occured while verifing authenticated content :- secret parameter for annotation preauthenticatecontent is empty"),
+	AUTHENTICATED_CONTENT_ANNOTATION_SECRET_ERROR("KER-WSC-107", "Error occured while verifying authenticated content :- secret parameter for annotation preauthenticatecontent is empty"),
 	
-	AUTHENTTICATED_CONTENT_ERROR("KER-WSC-108", "Error occured while verifing authenticated content :- content signature is not maching"),
+	AUTHENTICATED_CONTENT_ERROR("KER-WSC-108", "Error occured while verifying authenticated content :- content signature is not matching"),

Note: Also consider fixing "Error occured""Error occurred" and "verifing""verifying" throughout.

src/main/java/io/mosip/print/exception/ExceptionUtils.java-70-75 (1)

70-75: Remove unused private method getJsonValue.

The method has no references in the codebase and is dead code.

-	/**
-	 * This method provide jsonvalue based on propname mention.
-	 * 
-	 * @param node     the jsonnode.
-	 * @param propName the property name.
-	 * @return the property value.
-	 */
-	private static String getJsonValue(JsonNode node, String propName) {
-		if (node.get(propName) != null) {
-			return node.get(propName).asText();
-		}
-		return null;
-	}
-

Committable suggestion skipped: line range outside the PR's diff.

src/main/java/io/mosip/print/constant/QrcodeExceptionConstants.java-12-12 (1)

12-12: Typo in error message: "occured" should be "occurred".

🔎 Proposed fix
-	QRCODE_GENERATION_EXCEPTION("KER-QRG-001", "exception occured while writing QR code "),
+	QRCODE_GENERATION_EXCEPTION("KER-QRG-001", "exception occurred while writing QR code"),
src/main/java/io/mosip/print/entity/MspCardEntity.java-67-71 (1)

67-71: Type mismatch with existing entity for date fields.

The registrationDate and downloadDate fields use LocalDateTime, while PrintcardMCE uses Timestamp and Date respectively for the same columns. This inconsistency could cause data conversion issues.

src/test/java/io/mosip/print/test/util/UinCardGeneratorImplTest.java-93-100 (1)

93-100: Resource leak in FileOutputStream.

The FileOutputStream is not properly closed if an exception occurs between lines 93-96. Use try-with-resources.

🔎 Proposed fix
-	FileOutputStream op = new FileOutputStream(OutPutPdfFile);
-	op.write(bos);
-	op.flush();
-	assertTrue(OutPutPdfFile.exists());
-	if (op != null) {
-		op.close();
-	}
-	OutPutPdfFile.delete();
+	try (FileOutputStream op = new FileOutputStream(OutPutPdfFile)) {
+		op.write(bos);
+		op.flush();
+	}
+	assertTrue(OutPutPdfFile.exists());
+	OutPutPdfFile.delete();
src/main/java/io/mosip/print/util/CbeffToBiometricUtil.java-71-72 (1)

71-72: SLF4J logger doesn't support this multi-argument pattern.

The logging calls use a pattern like logger.debug(arg1, arg2, arg3, message) which doesn't work with SLF4J's standard parameterized logging. The extra arguments are likely being ignored.

🔎 Proposed fix using standard SLF4J pattern
-	printLogger.debug(LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.USERID.toString(), "",
-			"CbeffToBiometricUtil::getImageBytes()::entry");
+	printLogger.debug("CbeffToBiometricUtil::getImageBytes()::entry - sessionId={}, userId={}",
+			LoggerFileConstant.SESSIONID.toString(), LoggerFileConstant.USERID.toString());
src/main/java/io/mosip/print/util/PrintInstializer.java-81-86 (1)

81-86: Exception not logged in error handler.

The caught exception e is not included in the log, making debugging difficult.

🔎 Proposed fix
 	} catch (Exception e) {
-		logger.error(LoggerFileConstant.SESSIONID.toString(), PRINTINSTIALIZER, ONAPPLICATIONEVENT,
-				"Initializing subscribptions failed: ");
+		logger.error("Initializing subscriptions failed", e);

 		return false;
 	}

Committable suggestion skipped: line range outside the PR's diff.

src/main/java/io/mosip/print/service/impl/PrintServiceImpl.java-530-537 (1)

530-537: Remove the unused generateQrCode private method.

This method is never called anywhere in the codebase and simply duplicates the logic from setQrCode. It should be deleted.

Comment on lines +67 to +73
run: |
IMAGE_ID=$NAMESPACE/$SERVICE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
echo "push version ${{steps.getPomVersion.outputs.info}}"
VERSION=$BRANCH_NAME
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix undefined property reference.

Line 72 references ${{steps.getPomVersion.outputs.info}}, but there is no step with id getPomVersion defined in this workflow. This will cause the workflow to fail or use an empty string.

🔎 Proposed fix

Either remove the unused echo statement referencing the undefined property:

     - name: Push image
       run: |
         IMAGE_ID=$NAMESPACE/$SERVICE_NAME
 
         # Change all uppercase to lowercase
         IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
-        echo "push version ${{steps.getPomVersion.outputs.info}}"
         VERSION=$BRANCH_NAME

Or add the missing step to extract version from pom.xml if needed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run: |
IMAGE_ID=$NAMESPACE/$SERVICE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
echo "push version ${{steps.getPomVersion.outputs.info}}"
VERSION=$BRANCH_NAME
run: |
IMAGE_ID=$NAMESPACE/$SERVICE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
VERSION=$BRANCH_NAME
🧰 Tools
🪛 actionlint (1.7.9)

67-67: property "getpomversion" is not defined in object type {}

(expression)

🤖 Prompt for AI Agents
In .github/workflows/manual_push_trigger.yml around lines 67 to 73, the echo
uses an undefined step id `${{steps.getPomVersion.outputs.info}}` which will be
empty/fail; remove or replace that reference — either delete the echo line
entirely or change it to reference an existing step output/env var that actually
provides the POM version (or add a step with id `getPomVersion` that extracts
the version into `outputs.info`) so the workflow no longer references a
nonexistent output.

Comment on lines +256 to +259
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Apache HttpClient 4.5.8 security vulnerabilities CVE

💡 Result:

Apache HttpClient 4.5.8 — known/security CVEs

  • CVE-2020-13956: improper handling of malformed authority in request URIs (can cause requests to be sent to the wrong host). Affects HttpClient < 4.5.13 — fixed in 4.5.13. [1][2]

Notes:

  • Public vulnerability listings for httpclient 4.5.8 show CVE-2020-13956 as the direct CVE; some sources also list additional older TLS/hostname-check issues in related components but those are not specific to 4.5.8 itself. [2][3]

Recommendation:

  • Upgrade to at least httpclient 4.5.13 (or the latest 4.5.x / 5.x release) to address CVE-2020-13956. Verify transitive dependencies for other CVEs. [1][2][3]

Sources:
[1] NVD entry — CVE-2020-13956.
[2] CVE Details — CVE-2020-13956 / Apache HttpClient 4.5.8.
[3] Maven Central / MVNRepository listing for httpclient 4.5.8 (vulnerabilities note).


Upgrade httpclient to 4.5.13 or later to address CVE-2020-13956.

Apache HttpClient 4.5.8 contains CVE-2020-13956, which allows improper handling of malformed authority in request URIs, potentially causing requests to be sent to the wrong host. Upgrade to version 4.5.13 or later to resolve this vulnerability.

🤖 Prompt for AI Agents
In pom.xml around lines 256 to 259, the project currently depends on
org.apache.httpcomponents:httpclient:4.5.8 which is vulnerable (CVE-2020-13956);
update the version to 4.5.13 or later in the dependency entry, ensure any
dependencyManagement or parent BOM entries are aligned (or override them) so the
upgraded version is used, then run mvn -U clean install (and mvn
dependency:tree) to confirm the newer httpclient is resolved and run tests to
verify no regressions.

Comment on lines +27 to +34
public static final int FINGER_FORMAT_IDENTIFIER = 0x46495200;

public static final int IRIS_FORMAT_IDENTIFIER = 0x49495200;

public static final int FACE_FORMAT_IDENTIFIER = 0x46495200;

// TODO Actual face identifier waiting for Face ISO image
// public static final int FACE_FORMAT_IDENTIFIER = 0x46414300;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix duplicate face format identifier.

Lines 27 and 31 both define 0x46495200 (Finger Image Record format). The commented alternative on line 34 (0x46414300, Face format) should be used for FACE_FORMAT_IDENTIFIER instead, as CBEFF face and finger identifiers must be distinct. This duplicate will cause incorrect behavior when processing face biometric data.

🔎 Proposed fix
  public static final int FINGER_FORMAT_IDENTIFIER = 0x46495200;

  public static final int IRIS_FORMAT_IDENTIFIER = 0x49495200;

- public static final int FACE_FORMAT_IDENTIFIER = 0x46495200;
+ public static final int FACE_FORMAT_IDENTIFIER = 0x46414300;
-
- // TODO Actual face identifier waiting for Face ISO image
- // public static final int FACE_FORMAT_IDENTIFIER = 0x46414300;
🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/constant/CbeffConstant.java around lines 27 to
34, the FACE_FORMAT_IDENTIFIER is incorrectly set to the same value as
FINGER_FORMAT_IDENTIFIER (0x46495200); replace the duplicate with the correct
face identifier value 0x46414300 (as shown in the commented TODO) so that face
and finger CBEFF identifiers are distinct, and remove or update the TODO/comment
accordingly to reflect the change.

Comment on lines +131 to +142
public String getErrorCode() {
return infoItems.get(0).errorCode;
}

/**
* Return the last exception message.
*
* @return the last exception message
*/
public String getErrorText() {
return infoItems.get(0).errorText;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getErrorCode() and getErrorText() will throw IndexOutOfBoundsException when infoItems is empty.

When the exception is constructed using the no-arg or single-string constructors (lines 36-47), infoItems remains empty, causing these methods to fail.

🔎 Proposed fix
 	public String getErrorCode() {
+		if (infoItems.isEmpty()) {
+			return null;
+		}
 		return infoItems.get(0).errorCode;
 	}

 	/**
 	 * Return the last exception message.
 	 * 
 	 * @return the last exception message
 	 */
 	public String getErrorText() {
+		if (infoItems.isEmpty()) {
+			return getMessage();
+		}
 		return infoItems.get(0).errorText;
 	}
🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/exception/BaseCheckedException.java around lines
131-142, getErrorCode() and getErrorText() currently index infoItems.get(0)
which throws IndexOutOfBoundsException when infoItems is empty; update both
methods to first check if infoItems is non-empty and return a safe default
(e.g., null or empty string) when empty, or alternatively ensure the
no-arg/single-string constructors populate infoItems with a default InfoItem;
implement the simpler guard: if infoItems == null || infoItems.isEmpty() return
null (or ""), else return infoItems.get(0).errorCode / .errorText respectively.

Comment on lines +136 to +147
public String getErrorCode() {
return infoItems.get(0).errorCode;
}

/**
* Return the last exception message.
*
* @return the last exception message
*/
public String getErrorText() {
return infoItems.get(0).errorText;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

getErrorCode() and getErrorText() will throw IndexOutOfBoundsException when infoItems is empty.

Same issue as in BaseCheckedException: when using no-arg or single-string constructors, infoItems remains empty, causing these methods to fail.

🔎 Proposed fix
 	public String getErrorCode() {
+		if (infoItems.isEmpty()) {
+			return null;
+		}
 		return infoItems.get(0).errorCode;
 	}

 	/**
 	 * Return the last exception message.
 	 * 
 	 * @return the last exception message
 	 */
 	public String getErrorText() {
+		if (infoItems.isEmpty()) {
+			return getMessage();
+		}
 		return infoItems.get(0).errorText;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public String getErrorCode() {
return infoItems.get(0).errorCode;
}
/**
* Return the last exception message.
*
* @return the last exception message
*/
public String getErrorText() {
return infoItems.get(0).errorText;
}
public String getErrorCode() {
if (infoItems.isEmpty()) {
return null;
}
return infoItems.get(0).errorCode;
}
/**
* Return the last exception message.
*
* @return the last exception message
*/
public String getErrorText() {
if (infoItems.isEmpty()) {
return getMessage();
}
return infoItems.get(0).errorText;
}
🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/exception/BaseUncheckedException.java around
lines 136-147, getErrorCode() and getErrorText() directly access
infoItems.get(0) which will throw IndexOutOfBoundsException when infoItems is
empty; change each method to first check if infoItems is null or empty and
return null (or an empty string if project convention prefers) when empty,
otherwise return infoItems.get(0).errorCode / .errorText; keep the existing
return type and do not alter other logic.

Comment on lines +20 to +21
@Query("SELECT p.id FROM PrintcardMCE p where p.requestId1= ?1 ")
public Integer findIdByRequestId1(@Param("requestId1") String rid);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Return type mismatch: p.id is String, not Integer.

Same issue as sno() - the id field is a String but the method returns Integer.

🔎 Proposed fix
     @Query("SELECT p.id FROM PrintcardMCE p where p.requestId1= ?1 ")
-    public Integer findIdByRequestId1(@Param("requestId1") String rid);
+    public String findIdByRequestId1(@Param("requestId1") String rid);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Query("SELECT p.id FROM PrintcardMCE p where p.requestId1= ?1 ")
public Integer findIdByRequestId1(@Param("requestId1") String rid);
@Query("SELECT p.id FROM PrintcardMCE p where p.requestId1= ?1 ")
public String findIdByRequestId1(@Param("requestId1") String rid);
🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/service/PrintRepository.java around lines 20 to
21, the repository method findIdByRequestId1 is declared to return Integer but
the JPQL selects p.id which is a String; change the method signature to return
String (and adjust any callers if necessary) so the return type matches the
entity field type; ensure @Query and @Param remain the same and update
imports/usages accordingly.

Comment on lines +22 to +24
@Modifying
@Query("update PrintcardMCE p set p.status=?3 where p.requestId1= ?1 and p.status=?2 ")
public Integer updateExistingId(@Param("requestId1") String rid,@Param("status") int status,@Param("status") int newstatus);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Duplicate @Param("status") will cause parameter binding failure.

Both status and newstatus parameters use @Param("status"), which will cause incorrect binding. The second parameter should have a distinct name.

🔎 Proposed fix
     @Modifying
-    @Query("update PrintcardMCE p set p.status=?3 where p.requestId1= ?1 and p.status=?2 ")
-    public Integer updateExistingId(@Param("requestId1") String rid,@Param("status") int status,@Param("status") int newstatus);
+    @Query("update PrintcardMCE p set p.status=:newStatus where p.requestId1=:requestId1 and p.status=:status")
+    public Integer updateExistingId(@Param("requestId1") String rid, @Param("status") int status, @Param("newStatus") int newstatus);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Modifying
@Query("update PrintcardMCE p set p.status=?3 where p.requestId1= ?1 and p.status=?2 ")
public Integer updateExistingId(@Param("requestId1") String rid,@Param("status") int status,@Param("status") int newstatus);
@Modifying
@Query("update PrintcardMCE p set p.status=:newStatus where p.requestId1=:requestId1 and p.status=:status")
public Integer updateExistingId(@Param("requestId1") String rid, @Param("status") int status, @Param("newStatus") int newstatus);
🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/service/PrintRepository.java around lines 22-24,
the repository method uses duplicate @Param("status") annotations causing
binding failure; change the third parameter annotation to a distinct name (e.g.,
@Param("newstatus") int newstatus) and update the @Query to reference named
parameters (or matching positional indices) accordingly (for example use
p.requestId1 = :requestId1 and p.status = :status and set p.status = :newstatus)
so each method parameter has a unique @Param name that matches the query.

Comment on lines +64 to +72
public PrivateKeyEntry loadP12() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, UnrecoverableEntryException {
KeyStore mosipKeyStore = KeyStore.getInstance("PKCS12");
InputStream in = getClass().getClassLoader().getResourceAsStream("partner.p12");
mosipKeyStore.load(in, "qwerty@123".toCharArray());
ProtectionParameter password = new PasswordProtection("qwerty@123".toCharArray());
PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) mosipKeyStore.getEntry("partner", password);
return privateKeyEntry;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical security issue: Hardcoded credentials and keystore path.

The password qwerty@123 and keystore file partner.p12 are hardcoded. This is a severe security vulnerability. Credentials should be externalized to environment variables, a secrets manager, or secure configuration.

🔎 Proposed fix
+	@Value("${mosip.print.partner.keystore.path}")
+	private String keystorePath;
+
+	@Value("${mosip.print.partner.keystore.password}")
+	private String keystorePassword;
+
+	@Value("${mosip.print.partner.key.alias}")
+	private String keyAlias;
+
 	public PrivateKeyEntry loadP12() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
 			IOException, UnrecoverableEntryException {
 		KeyStore mosipKeyStore = KeyStore.getInstance("PKCS12");
-		InputStream in = getClass().getClassLoader().getResourceAsStream("partner.p12");
-		mosipKeyStore.load(in, "qwerty@123".toCharArray());
-		ProtectionParameter password = new PasswordProtection("qwerty@123".toCharArray());
-		PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) mosipKeyStore.getEntry("partner", password);
+		try (InputStream in = getClass().getClassLoader().getResourceAsStream(keystorePath)) {
+			mosipKeyStore.load(in, keystorePassword.toCharArray());
+		}
+		ProtectionParameter password = new PasswordProtection(keystorePassword.toCharArray());
+		PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) mosipKeyStore.getEntry(keyAlias, password);
 		return privateKeyEntry;
 	}
🤖 Prompt for AI Agents
src/main/java/io/mosip/print/util/CryptoCoreUtil.java around lines 64 to 72, the
method currently hardcodes the keystore filename and password which is a
security risk; change it to read the keystore path/name and password from a
secure external source (e.g., environment variables, application config, or a
secrets manager) instead of literals, validate that the values exist and fail
fast with a clear error if not provided, obtain the password as a char[] (not a
String) from the secure source, open the keystore InputStream via a
try-with-resources so it is always closed, and use the externally provided
path/stream and password when loading the KeyStore and retrieving the
PrivateKeyEntry.

Comment on lines +12 to +17
public static String readFileFromResources(String filename) throws URISyntaxException, IOException {
InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename);
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, "UTF-8");
return writer.toString();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Null pointer exception and resource leak.

This method has multiple critical issues:

  1. NPE risk: Line 13's getResourceAsStream returns null if the resource is not found, but line 15 uses inputStream without a null check, causing a NullPointerException.
  2. Resource leak: The InputStream is never closed, violating proper resource management.
  3. Misleading signature: URISyntaxException is declared but never thrown by any code in this method.
🔎 Proposed fix using try-with-resources
-    public static String readFileFromResources(String filename) throws URISyntaxException, IOException {
-        InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename);
-        StringWriter writer = new StringWriter();
-        IOUtils.copy(inputStream, writer, "UTF-8");
-        return writer.toString();
+    public static String readFileFromResources(String filename) throws IOException {
+        try (InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename)) {
+            if (inputStream == null) {
+                throw new IOException("Resource not found: " + filename);
+            }
+            StringWriter writer = new StringWriter();
+            IOUtils.copy(inputStream, writer, "UTF-8");
+            return writer.toString();
+        }
     }

Alternatively, use IOUtils.toString() for a more concise implementation:

-    public static String readFileFromResources(String filename) throws URISyntaxException, IOException {
-        InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename);
-        StringWriter writer = new StringWriter();
-        IOUtils.copy(inputStream, writer, "UTF-8");
-        return writer.toString();
+    public static String readFileFromResources(String filename) throws IOException {
+        try (InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename)) {
+            if (inputStream == null) {
+                throw new IOException("Resource not found: " + filename);
+            }
+            return IOUtils.toString(inputStream, "UTF-8");
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static String readFileFromResources(String filename) throws URISyntaxException, IOException {
InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename);
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, "UTF-8");
return writer.toString();
}
public static String readFileFromResources(String filename) throws IOException {
try (InputStream inputStream = Helpers.class.getClassLoader().getResourceAsStream(filename)) {
if (inputStream == null) {
throw new IOException("Resource not found: " + filename);
}
return IOUtils.toString(inputStream, "UTF-8");
}
}
🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/util/Helpers.java around lines 12 to 17, the
method risks an NPE because getResourceAsStream may return null, leaks the
InputStream because it is never closed, and declares URISyntaxException which is
not thrown; fix by removing the unused URISyntaxException from the signature,
obtain the InputStream and immediately check for null and throw a clear
IOException (or FileNotFoundException) if the resource isn't found, then read
the stream inside a try-with-resources block (or use
IOUtils.toString(inputStream, "UTF-8") inside the try-with-resources) so the
stream is always closed; ensure any thrown exceptions are appropriate
(IOException) and update callers if the signature changes.

Comment on lines +40 to +56
public static boolean isValidBearerToken(String accessToken, String issuerUrl, String clientId) {

try {
DecodedJWT decodedJWT = JWT.decode(accessToken);
Map<String, Claim> claims = decodedJWT.getClaims();
LocalDateTime expiryTime = DateUtils
.convertUTCToLocalDateTime(DateUtils.getUTCTimeFromDate(decodedJWT.getExpiresAt()));

if (!decodedJWT.getIssuer().equals(issuerUrl)) {
return false;
} else if (!DateUtils.before(DateUtils.getUTCCurrentDateTime(), expiryTime)) {
return false;
} else if (!claims.get("clientId").asString().equals(clientId)) {
return false;
} else {
return true;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Security: JWT signature is not verified.

JWT.decode() only decodes the token without verifying its cryptographic signature. An attacker could forge a token with arbitrary claims. Use JWT.require(...).build().verify(accessToken) to validate the signature with the issuer's public key.

🔎 Proposed fix outline
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.JWTVerifier;
+
 public static boolean isValidBearerToken(String accessToken, String issuerUrl, String clientId) {
     try {
-        DecodedJWT decodedJWT = JWT.decode(accessToken);
+        // Fetch public key from issuer's JWKS endpoint and create Algorithm
+        Algorithm algorithm = Algorithm.RSA256(rsaPublicKey, null);
+        JWTVerifier verifier = JWT.require(algorithm)
+            .withIssuer(issuerUrl)
+            .withClaim("clientId", clientId)
+            .build();
+        DecodedJWT decodedJWT = verifier.verify(accessToken);
+        return true;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/io/mosip/print/util/TokenHandlerUtil.java around lines 40 to
56, the code uses JWT.decode(accessToken) which only decodes JWTs and does not
verify their signatures; replace the decode call with a verified decode using
JWT.require(...).build().verify(accessToken) configured with the correct
Algorithm (public key or shared secret) for the issuer, validate the returned
DecodedJWT only after verification, and catch JWTVerificationException to return
false on verification failure; also keep the existing issuer, expiry and
clientId checks but ensure null-safety for claims and decodedJWT before
accessing fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants