-
Notifications
You must be signed in to change notification settings - Fork 0
JPostman Tutorial Part 4: Secure API Logs with jpostman secure
Video tutorial: https://www.youtube.com/watch?v=zOci7u021oU
In this tutorial, we introduce the jpostman-secure module and show how to make API test logs safer.
When API tests fail, request and response logs are useful for troubleshooting, but they can also expose sensitive values such as passwords, access tokens, refresh tokens, cookies, API keys, emails, bank data, browser data, and user profile details.
The secure module helps protect that output while still keeping the logs useful for debugging.
You will learn how to wrap regular JPostman requests and responses with SecureRequest, SecureResponse, and SecureContext, how to mask sensitive values, how to control response headers, and how to move reusable masking rules into an INI policy file.
By the end of this tutorial, you will know how to:
- Add the
jpostman-securedependency to a Maven test project. - Wrap a regular
RequestwithSecureRequest. - Wrap a regular
ApiResponsewithSecureResponse. - Use
SecureContextto share secure configuration across requests. - Mask sensitive request and response values.
- Print unresolved placeholders with
log(false). - Print resolved secure logs with
log(). - Store environment values as plain or protected values.
- Use
secret(env)to protect environment values. - Use
unsecret(...)to expose selected values such asbase_url. - Use
headers(...)to protect response or request headers. - Use
headersFilter(...)to reduce response header output. - Use
redact(...)rules for body fields, JSON paths, wildcard paths, regex rules, and partial masking. - Load reusable secure policy profiles from an INI file.
- Apply profile-specific rules with
loadRules(...). - Use
filter(...)to reduce response body output.
Before starting this tutorial, make sure you completed the previous tutorials:
- Part 1: Create a Java Maven API testing project with JPostman.
- Part 2: Execute REST API requests using the RestAssured adapter.
- Part 3: Chain API requests using an access token.
You should already have:
- A Maven test project.
- Exported Postman collection JSON file.
- Exported Postman environment JSON file.
-
jpostman-coreconfigured. -
jpostman-restassuredconfigured. - A working TestNG test that executes a login request and an authenticated user request.
Open your pom.xml file and add the jpostman-secure dependency.
Example:
To manage dependency versions with Maven properties, define the version once:
<properties>
<java.version>11</java.version>
<maven.compiler.release>${java.version}</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jpostman.version>REPLACE_WITH_LATEST_VERSION</jpostman.version>
</properties>Then reference it from the dependency:
<dependency>
<groupId>io.github.jpostman</groupId>
<artifactId>jpostman-secure</artifactId>
<version>${jpostman.version}</version>
<scope>test</scope>
</dependency>After updating the POM, update the Maven project from your IDE.
In Eclipse:
- Right-click the project or
pom.xml. - Select Maven.
- Select Update Project.
- Click OK.
Import the secure classes:
import io.jpostman.secure.SecureContext;
import io.jpostman.secure.SecureRequest;
import io.jpostman.secure.SecureResponse;Create a secure context:
SecureContext secure = SecureContext.create();SecureContext stores secure configuration such as redaction rules, protected values, response filters, and protected headers.
Instead of using a regular Request directly, wrap it with the secure context:
SecureRequest reqToken = secure.from(
col.getRequest("Login user and get tokens")
.builder()
.build(env)
);This creates a SecureRequest that can be printed or logged safely.
The executor still returns a regular ApiResponse. Wrap it with the same secure context:
SecureResponse resToken = secure.from(RestAssuredExecutor.execute(reqToken));Now the response output can be protected before it is printed or added to an assertion message.
Request and response logs are especially useful when a test fails.
Instead of printing the raw response body, use secure logging:
assertEquals(resToken.statusCode(), 200, "Body Result: " + secure.log() + "\n");This keeps the failure message useful while masking protected values.
The secure module includes default rules for common sensitive values, but you can add your own rules.
Example:
SecureContext secure = SecureContext.create()
.redact("email");This masks fields named email in request or response logs.
Example output:
{
"email": "********"
}Use log(false) when you want to inspect unresolved placeholders:
secure.log(false);Example unresolved output:
Authorization = Bearer {{accessToken}}
Use log() when you want resolved values with masking applied:
secure.log();Example resolved secure output:
Authorization = Bearer ********
This is helpful when debugging missing environment variables or unresolved placeholders.
SecureContext can store values used for request resolution.
Use plain(env) when the values are safe to display:
SecureContext secure = SecureContext.create()
.plain(env);Use secret(env) when the values should be protected:
SecureContext secure = SecureContext.create()
.secret(env);With secret(env), resolved values are available for request execution, but they are masked in logs.
Sometimes one value should remain visible for debugging. For example, you may want to show base_url while still hiding tokens and passwords.
Use unsecret(...):
SecureContext secure = SecureContext.create()
.secret(env)
.unsecret("base_url");This keeps base_url available and visible while the other environment values remain protected.
Use headers(...) to mask specific request or response headers.
Example:
SecureContext secure = SecureContext.create()
.headers("Etag");If the response contains an Etag header, its value is masked:
Etag = ********
You can also use regex header rules:
SecureContext secure = SecureContext.create()
.headers("regex:.*cookie.*");Responses may include many headers that are not needed in test failure logs.
Use headersFilter(...) to show only selected headers:
SecureContext secure = SecureContext.create()
.headersFilter("Date");Only matching response headers are included in the secure response log.
You can also use regex:
SecureContext secure = SecureContext.create()
.headersFilter("regex:^x-.*");As rules grow, it is better to move them out of Java code and into a policy file.
Create a file under:
src/test/resources
Example file name:
demo_test_rule.ini
Example policy file:
# Default secure rules
[default]
unsecret=base_url
redact=email
headersFilter=Date
# User response rules
[user]
extends=default
# show only country code from phone values
redact=phone[regex:^\+\d{1,2}]
# hide user address only
redact=/address/address
# hide additional user details
redact=ip,bloodGroup,height,weight,eyeColor,/hair
# hide location coordinates and MAC address
redact=/**/coordinates,macAddress
# hide bank details and keep only last 4 characters
redact=cardNumber[-4:],iban[-4:]
# hide identity, browser, and crypto data
redact=ssn[-3:],userAgent,/cryptoThe [default] section is applied when the policy file is loaded.
The [user] section extends [default] and adds response-specific rules for the authenticated user response.
Load the policy file from test resources:
SecureContext secure = SecureContext.create()
.secret(env)
.load(getClass().getResourceAsStream("demo_test_rule.ini"));The default rules are applied immediately.
Before executing or logging the authenticated user request, apply the [user] profile:
secure = secure.loadRules("user");loadRules(...) returns a copied context with the selected rules applied. The original context is not changed.
This makes it easier to reuse a base secure context and apply different rules for different API flows.
Example:
SecureContext loginSecure = secure.loadRules("login");
SecureContext userSecure = secure.loadRules("user");Policy rules support exact keys, slices, paths, wildcards, recursive wildcards, and regex matching.
# Exact key rule
redact=email
# Keep the last 4 characters
redact=cardNumber[-4:]
# Keep only the value part matching a regex
redact=phone[regex:^\+\d{1,2}]
# JSON path rule
redact=/address/address
# Wildcard path rule
redact=/products/*/reviews
# Recursive wildcard path rule
redact=/**/coordinates
# Regex key or path rule
redact=regex:.*token.*After protecting sensitive data, you can reduce the response body to only the fields needed for debugging.
Example:
secure.filter("id", "firstName", "lastName", "gender");This keeps the log shorter and easier to read.
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import io.jpostman.ApiResponse;
import io.jpostman.Collection;
import io.jpostman.Environment;
import io.jpostman.JPostman;
import io.jpostman.JPostman.Context;
import io.jpostman.secure.SecureContext;
import io.jpostman.secure.SecureRequest;
import io.jpostman.secure.SecureResponse;
import io.jpostman.restassured.RestAssuredExecutor;
public class DemoTest {
@Test
public void test1() throws Exception {
Context cxt = JPostman
.fromResources()
.collection("DummyJSON.postman_collection.json")
.environment("DummyJSON.postman_environment.json")
.build();
Environment env = cxt.getEnvironment();
Collection col = cxt.getCollection();
SecureContext secure = SecureContext.create()
.secret(env)
.load(getClass().getResourceAsStream("demo_test_rule.ini"));
SecureRequest reqToken = secure.from(
col.getRequest("Login user and get tokens")
.builder()
.build()
);
SecureResponse resToken = secure.from(RestAssuredExecutor.execute(reqToken));
assertEquals(resToken.statusCode(), 200, "Body Result: " + secure.log() + "\n");
secure = secure.loadRules("user");
SecureRequest reqUser = secure.from(
col.getRequest("Get current auth user")
.builder()
.build()
);
ApiResponse rawUser = RestAssuredExecutor
.apply(reqUser)
.auth()
.oauth2(resToken.path("accessToken"))
.response();
SecureResponse resUser = secure.from(rawUser);
assertEquals(resUser.statusCode(), 200, "Body Result: " + secure.log() + "\n");
}
}Update the collection, environment, and policy file names if your files use different names.
This test performs a secure chained API flow:
- Loads the Postman collection.
- Loads the Postman environment.
- Stores environment values as protected values with
secret(env). - Loads reusable secure rules from
demo_test_rule.ini. - Executes the login request through
SecureRequest. - Wraps the login response as
SecureResponse. - Validates the login status code.
- Applies the
[user]secure policy profile. - Executes the authenticated user request.
- Wraps the user response as
SecureResponse. - Validates the user request status code.
- Uses secure logging for safer assertion output.
Secure logs help API test automation teams troubleshoot failures without exposing sensitive data.
This is useful when logs are stored in:
- Local console output.
- CI/CD build logs.
- Cloud log platforms.
- Test reports.
- Shared issue attachments.
With jpostman-secure, you can keep enough data for debugging while masking values that should not be exposed.
In this tutorial, we added the jpostman-secure module, wrapped requests and responses with secure classes, protected environment values, masked sensitive response fields, filtered headers, and moved reusable masking rules into an INI policy file.
We also used loadRules(...) to apply a named policy profile for the authenticated user response and used filter(...) to reduce response output.
In the next tutorial, we will review additional JPostman integrations such as Vault, Kubernetes ConfigMap and Secret, and GitHub environment support.
- JPostman API Documentation: https://jpostman.github.io/jpostman/
- JPostman GitHub repository: https://github.com/JPostman/jpostman
- JPostman Wiki: https://github.com/JPostman/jpostman/wiki
- Secure rule policy template: https://raw.githubusercontent.com/JPostman/jpostman/refs/heads/main/jpostman-secure/src/test/resources/secure-rules.ini
- DummyJSON API: https://dummyjson.com