Package Exports
- penjaro-network-security-library-javascript
- penjaro-network-security-library-javascript/dist/library.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (penjaro-network-security-library-javascript) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Penjaro Network Security Middleware (JavaScript/TypeScript)
Penjaro provides intelligent, proactive network security for your web applications and APIs. It acts as a smart shield, analyzing incoming requests and outgoing responses using a combination of rule-based detection, threat intelligence feeds, AI-powered analysis, and framework-specific hardening techniques.
Stop attacks before they reach your application logic. Penjaro integrates seamlessly as middleware or a standalone proxy, offering a powerful layer of defense against common and sophisticated threats.
Why Penjaro?
- Proactive Defense: Identifies and blocks threats like SQL injection, XSS, command injection, malicious bots, DoS attempts, and more at the edge.
- AI-Powered Intelligence: (Optional) Leverage TensorFlow.js models to detect novel and complex attack patterns that signature-based methods might miss.
- Threat Feed Integration: Stay ahead of attackers by automatically blocking IPs known for malicious activity (malware C&C, scanners, Tor exits, etc.).
- Response Security: Prevent accidental leaks of sensitive data (API keys, PII) or verbose error messages in responses.
- Framework Native: Easy integration with popular Node.js frameworks (Express, Koa, NestJS, Fastify, Hapi).
- Highly Configurable: Fine-tune security policies to match your application's specific needs and risk profile.
- Distributed Ready: Use Redis to share rate limiting and threat intelligence state across multiple instances.
Features
- Request Filtering:
- Rule-based detection (SQLi, XSS, Cmd Inj, Path Trav, SSRF, Log4j, etc.)
- Customizable rule patterns
- Threat intelligence IP blocking (Tor, FireHOL, Feodo, CINS, custom feeds)
- IP Rate Limiting (in-memory or Redis)
- JA3 TLS Fingerprint blocking (requires upstream proxy setup)
- GraphQL Protection (depth, complexity, introspection, keyword blocking)
- Response Filtering (Proxy Mode Only):
- Data leak prevention (detects common key/PII patterns)
- Verbose error message scrubbing
- Customizable detection patterns
- AI-Powered Analysis (Optional):
- Integrates with pre-trained
tf.jsmodels. - Customizable feature extraction.
- Configurable failure modes (fail-open/fail-closed).
- Integrates with pre-trained
- Incident Reporting:
- Reports blocked requests/responses to the Penjaro API for visibility.
- Flexible Deployment:
- Middleware Mode: Integrate directly into your application code.
- Proxy Mode: Run as a standalone reverse proxy shielding your backend.
Installation
npm install penjaro-network-security-library-javascript
# or
yarn add penjaro-network-security-library-javascript
# Install peer dependencies for your framework (e.g., Express)
npm install express
# or yarn add express
# Optional dependencies (needed for specific features)
npm install redis # For Redis-based rate limiting / threat intel
npm install graphql # For GraphQL protection
npm install @tensorflow/tfjs-node # For AI featuresCore Concept: Operating Modes
Penjaro can operate in two distinct modes, determined by the presence of the targetServer option:
Middleware Mode (No
targetServer):- Integrates directly into your framework's middleware chain (e.g.,
app.use()in Express/Koa,consumer.apply()in NestJS). - Analyzes incoming requests only.
- Blocks malicious requests or passes legitimate ones to your application logic (
next()). - Ideal for securing existing applications without changing infrastructure.
- Integrates directly into your framework's middleware chain (e.g.,
Proxy Mode (
targetServeris provided):- Acts as a reverse proxy. It receives all traffic, analyzes incoming requests, forwards valid ones to your
targetServer, analyzes outgoing responses from the target, and then sends the final response to the client. - Provides both request and response security.
- Requires Penjaro to run as a separate process or container listening on your public-facing port.
- Offers the most comprehensive protection but involves a slightly different deployment pattern.
- Acts as a reverse proxy. It receives all traffic, analyzes incoming requests, forwards valid ones to your
Getting Started
Penjaro is initialized asynchronously. You'll typically call createPenjaroProxy (for Express) or createPenjaroAdapter (for other frameworks) within an async context.
Basic Express Example (Middleware Mode)
import express from 'express';
import { createPenjaroProxy, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript';
// Adjust path if needed based on your setup, might be just 'penjaro-network-security-library-javascript' after install
const app = express();
const penjaroOptions: PenjaroConfigOptions = {
apiKey: process.env.PENJARO_API_KEY || 'YOUR_API_KEY', // REQUIRED! Get from Penjaro dashboard
// Middleware mode - no targetServer specified
enableRateLimit: true,
enableThreatIntelCheck: true,
includeDefaultFireholFeed: true,
// Add other options as needed
};
async function startServer() {
try {
// Initialize Penjaro middleware
const penjaroMiddleware = await createPenjaroProxy(penjaroOptions);
// Apply Penjaro middleware BEFORE other routes/middleware
app.use(penjaroMiddleware);
// Your application routes
app.get('/', (req, res) => {
res.send('Hello World!');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running with Penjaro protection on port ${PORT}`);
});
} catch (error) {
console.error("Failed to initialize Penjaro or start server:", error);
process.exit(1);
}
}
startServer();Using the Adapter Factory (Recommended for Non-Express)
For Koa, NestJS, Fastify, and Hapi, use the createPenjaroAdapter factory:
import { createPenjaroAdapter, Framework, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript/dist/adapters';
// Adjust path if needed
const penjaroOptions: PenjaroConfigOptions = {
apiKey: process.env.PENJARO_API_KEY || 'YOUR_API_KEY',
// Add other desired options...
};
async function initializePenjaro(frameworkType: Framework) {
try {
// Create the adapter for your specific framework
const adapter = await createPenjaroAdapter(frameworkType, penjaroOptions);
return adapter;
} catch (error) {
console.error(`Failed to create Penjaro adapter for ${frameworkType}:`, error);
throw error; // Re-throw or handle appropriately
}
}
// Example usage within framework setup (see Framework Integration Examples below)
// const koaMiddleware = await initializePenjaro(Framework.KOA);
// app.use(koaMiddleware); Configuration (PenjaroConfigOptions)
The behavior of Penjaro is controlled by the PenjaroConfigOptions object passed during initialization.
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
REQUIRED | Your unique API key obtained from the Penjaro dashboard. Used for validation and incident reporting. |
targetServer |
string |
undefined |
If set (e.g., 'http://localhost:8080'), enables Proxy Mode. If omitted, enables Middleware Mode. |
aiModel |
tf.LayersModel or compatible |
null |
A pre-loaded TensorFlow.js model for AI analysis. If null, AI features are disabled. See 'AI Model Handling'. |
proxyConfig |
object |
{} |
Options passed directly to http-proxy-middleware (Proxy Mode only). See HPM Docs. |
enableRateLimit |
boolean |
true |
Enables basic IP-based rate limiting. Uses in-memory store by default, or Redis if configured. |
rateLimitPoints |
number |
10 |
Max requests allowed per IP per window duration. |
rateLimitDuration |
number |
1 |
Time window duration in seconds. |
enableResponseAnalysis |
boolean |
true |
Master switch for response analysis (Proxy Mode only). Performance cost: buffers responses, breaks streaming. |
enableDataLeakChecks |
boolean |
true |
Enables regex checks for common PII/key patterns in responses. Requires enableResponseAnalysis. |
enableVerboseErrorChecks |
boolean |
true |
Enables regex checks for stack traces/verbose errors in responses. Requires enableResponseAnalysis. |
responseAnalysisMaxSize |
number |
1048576 (1MB) |
Maximum response body size (bytes) to buffer and analyze. |
responseAnalysisContentTypes |
string[] |
['application/json', 'text/html', ...] |
Array of Content-Type headers (case-insensitive) for which response analysis should run. |
blockUnsafeResponses |
boolean |
true |
If true, blocks responses that fail data leak or verbose error checks. |
replaceDefaultPatterns |
boolean |
false |
If true, customDataLeakPatterns and customVerboseErrorPatterns replace the defaults. If false, they are merged. |
customDataLeakPatterns |
RegExp[] |
[] |
Add your own regex patterns to detect sensitive data leaks in responses. |
customVerboseErrorPatterns |
RegExp[] |
[] |
Add your own regex patterns to detect verbose errors in responses. |
enableJa3Check |
boolean |
false |
Enables blocking based on TLS JA3 fingerprint. Requires upstream proxy (Nginx, Envoy) to inject the hash. See 'JA3 Fingerprinting'. |
ja3HeaderName |
string |
'X-JA3-Hash' |
HTTP Header name (case-insensitive) containing the JA3 hash provided by the upstream proxy. |
badJa3Hashes |
string[] |
[...] (List of known bad hashes) |
Array of malicious JA3 hashes to block if enableJa3Check is true. |
enableGraphqlProtection |
boolean |
false |
Enables GraphQL-specific checks (depth, complexity, introspection, keywords). Requires graphql package. See 'GraphQL Protection'. |
graphqlMaxDepth |
number |
10 |
Maximum allowed query nesting depth. |
graphqlMaxComplexity |
number |
1000 |
Maximum allowed query complexity score (approximated by field count). |
graphqlBlockIntrospection |
boolean |
true |
Blocks GraphQL introspection queries (__schema, __type). Set false for development if needed. |
graphqlBlockKeywords |
string[] |
[] |
Block requests if these keywords appear in GraphQL string arguments/values (e.g., '../', 'SELECT'). |
enableThreatIntelCheck |
boolean |
false |
Enables checking the source IP against threat intelligence feeds. Uses in-memory store by default, or Redis if configured. |
threatIntelFeedUrls |
string[] |
[] |
Array of custom URLs pointing to plain text IP blocklist feeds (one IP per line). |
threatIntelFetchIntervalMinutes |
number |
60 |
How often (in minutes) to refresh the threat intelligence feeds. |
includeDefaultTorFeed |
boolean |
false |
Include the Tor exit node list from check.torproject.org. |
includeDefaultFireholFeed |
boolean |
false |
Include the FireHOL Level 1 blocklist (raw.githubusercontent.com/firehol/blocklist-ipsets). |
includeDefaultFeodoFeed |
boolean |
false |
Include the Feodo Tracker IP blocklist (feodotracker.abuse.ch). |
includeDefaultCinsFeed |
boolean |
false |
Include the CINS Army List (cinsscore.com). |
whitelistIps |
string[] |
[] |
List of IP addresses that should NEVER be blocked, even if they appear in threat intelligence feeds. Prevents false positives for trusted sources. |
onThreatIntelError |
(error: Error) => void |
undefined |
Optional callback function invoked if an error occurs during threat feed fetching or parsing. |
excludePathsFromDataLeakCheck |
RegExp[] |
[] |
URL paths to exclude from data leak checks (e.g., /auth endpoints where emails are expected). Reduces false positives. |
excludePathsFromErrorCheck |
RegExp[] |
[] |
URL paths to exclude from verbose error checks. |
redisClient |
RedisClientType (v4) |
undefined |
Provide a pre-configured redis v4 client instance. Takes precedence over redisUrl/redisOptions. Enables shared state for Rate Limiting & Threat Intel. |
redisUrl |
string |
undefined |
Redis connection URL (e.g., 'redis://user:pass@host:port/db'). Used if redisClient is not provided. |
redisOptions |
object |
undefined |
Redis connection options (passed to createClient). Used if redisClient and redisUrl are not provided. |
disableDefaultRequestRules |
object |
{} |
Disable specific built-in request rule categories. E.g., { sql: true, xss: true }. Categories: sql, xss, command, traversal, header, ssrf, log4j, info, scanner, ssrfHeader, xssLib. |
customRequestRulePatterns |
object |
{} |
Add custom request rule regex patterns, keyed by category name. E.g., { myRule: [/bad_pattern/i] }. |
replaceDefaultRequestPatterns |
boolean |
false |
If true, customRequestRulePatterns replace the defaults per category. If false, they are merged. |
featureExtractor |
(req: Request) => number[] |
undefined |
Custom function to extract features from an Express Request object for the AI model. See 'AI Model Handling'. |
aiAnalysisFailureMode |
'fail-open' | 'fail-closed' |
'fail-open' |
Behavior if the AI model analysis fails: 'fail-open' allows the request, 'fail-closed' blocks it. |
enableIncidentReporting |
boolean |
true |
If true, reports blocked requests/responses and security events to the Penjaro API (using your apiKey). |
Framework Integration Examples
Always initialize Penjaro early in your application setup, typically before defining routes or applying other middleware that might parse bodies.
Express (Native)
Use createPenjaroProxy directly.
// See "Getting Started" example above
import express from 'express';
import { createPenjaroProxy, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript';
const app = express();
const penjaroOptions: PenjaroConfigOptions = { /* ... your config ... */ apiKey: '...' };
async function start() {
const penjaroMiddleware = await createPenjaroProxy(penjaroOptions);
app.use(penjaroMiddleware);
// ... rest of your express app setup ...
app.listen(3000);
}
start();Koa
Use createPenjaroAdapter with Framework.KOA.
import Koa from 'koa';
import { createPenjaroAdapter, Framework, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript/dist/adapters';
const app = new Koa();
const penjaroOptions: PenjaroConfigOptions = { /* ... your config ... */ apiKey: '...' };
async function start() {
const penjaroMiddleware = await createPenjaroAdapter(Framework.KOA, penjaroOptions);
app.use(penjaroMiddleware);
// ... rest of your koa app setup ...
app.listen(3000);
}
start();NestJS
Use createPenjaroAdapter with Framework.NESTJS inside your AppModule's configure method.
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { createPenjaroAdapter, Framework, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript/dist/adapters';
// Define your options (potentially load from config service)
const penjaroOptions: PenjaroConfigOptions = {
apiKey: process.env.PENJARO_API_KEY || 'YOUR_API_KEY',
// ... other options
};
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
async configure(consumer: MiddlewareConsumer) {
// Create the NestJS compatible middleware function
const PenjaroMiddleware = await createPenjaroAdapter(Framework.NESTJS, penjaroOptions);
consumer
.apply(PenjaroMiddleware)
.forRoutes('*'); // Apply to all routes, or specify paths/methods
// .forRoutes({ path: 'protected/*', method: RequestMethod.ALL });
}
}Fastify
Use createPenjaroAdapter with Framework.FASTIFY and register it as a plugin.
import Fastify from 'fastify';
import { createPenjaroAdapter, Framework, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript/dist/adapters';
const fastify = Fastify({ logger: true });
const penjaroOptions: PenjaroConfigOptions = { /* ... your config ... */ apiKey: '...' };
async function start() {
const penjaroPlugin = await createPenjaroAdapter(Framework.FASTIFY, penjaroOptions);
// Register Penjaro plugin - ensure it runs early (e.g., before routes)
await fastify.register(penjaroPlugin);
// Define routes *after* registering Penjaro
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
});
// ... rest of your fastify app setup ...
await fastify.listen({ port: 3000 });
}
start();Hapi
Use createPenjaroAdapter with Framework.HAPI and register it as a plugin.
import Hapi from '@hapi/hapi';
import { createPenjaroAdapter, Framework, PenjaroConfigOptions } from 'penjaro-network-security-library-javascript/dist/adapters';
const penjaroOptions: PenjaroConfigOptions = { /* ... your config ... */ apiKey: '...' };
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
const penjaroPluginObject = await createPenjaroAdapter(Framework.HAPI, penjaroOptions);
// Register Penjaro plugin
await server.register(penjaroPluginObject);
// Define routes *after* registering Penjaro
server.route({
method: 'GET',
path:'/',
handler: (request, h) => {
return 'Hello World!';
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();Key Feature Deep Dive
False Positive Prevention
Penjaro includes several mechanisms to prevent false positives while maintaining robust security:
Request Whitelisting: Built-in whitelist patterns automatically allow known legitimate patterns like OAuth/OIDC callback URLs that might otherwise trigger security alarms.
IP Whitelisting: Provide a list of trusted IP addresses via the
whitelistIpsoption to ensure they're never blocked by the threat intelligence module, even if they appear in block lists.Path Exclusions for Response Analysis: Use
excludePathsFromDataLeakCheckandexcludePathsFromErrorCheckto skip certain checks on specific paths. For example:const penjaroOptions: PenjaroConfigOptions = { apiKey: '...', // Exclude user profile endpoints from data leak checks excludePathsFromDataLeakCheck: [ /\/api\/user\/profile/i, /\/auth\//i ], // Exclude development/debug endpoints from error checks excludePathsFromErrorCheck: [ /\/api\/debug\//i ] };
Context-Aware Analysis: The response analyzer automatically adjusts its checks based on the request context. For example, email patterns aren't flagged as potential data leaks on authentication endpoints where emails are expected.
AI Model Handling
- Enable: Provide a loaded TensorFlow.js model (
tf.LayersModelor compatible object with apredictmethod) via theaiModeloption. - Loading: You are responsible for loading the model before initializing Penjaro. Example using
@tensorflow/tfjs-node:import * as tf from '@tensorflow/tfjs-node'; // Or '@tensorflow/tfjs' for browser/React Native environments if applicable async function loadMyModel() { try { const model = await tf.loadLayersModel('file://path/to/your/model/model.json'); // Or tf.loadGraphModel, or load from other sources console.log("AI Model loaded successfully."); return model; } catch (error) { console.error("Failed to load AI model:", error); return null; // Handle error appropriately } } // Then, in your Penjaro init: const loadedModel = await loadMyModel(); const penjaroOptions: PenjaroConfigOptions = { apiKey: '...', aiModel: loadedModel, // ... other options }; // const penjaroMiddleware = await createPenjaroProxy(penjaroOptions);
- Feature Extraction: By default, Penjaro might use a basic feature extractor if none is provided. For optimal results, supply a
featureExtractorfunction in the options. This function receives the framework's request object (currently assumes ExpressRequesttype) and must return an array of numbers (number[]) suitable as input for your model'spredictmethod.import { Request } from 'express'; // Or the relevant type for your framework if adapters evolve function myFeatureExtractor(req: Request): number[] { // Example: Extract numerical features from headers, body, query params const contentLength = parseInt(req.headers['content-length'] || '0', 10); const queryParamCount = Object.keys(req.query).length; // ... more sophisticated feature engineering ... const features = [contentLength, queryParamCount /*, ... */]; // Ensure the output array shape matches your model's expected input return features; } const penjaroOptions: PenjaroConfigOptions = { // ... aiModel: loadedModel, featureExtractor: myFeatureExtractor, };
- Failure Mode: Use
aiAnalysisFailureMode('fail-open'or'fail-closed') to decide if requests should be allowed or blocked if the AI analysis encounters an error.
Threat Intelligence
- Enable: Set
enableThreatIntelCheck: true. - Feeds: Include default feeds (
includeDefaultTorFeed, etc.) and/or provide your own viathreatIntelFeedUrls. Custom feeds must be plain text URLs with one IP address per line. - State: By default, the fetched blocklist is stored in memory. For multi-instance deployments, provide a Redis client (
redisClient,redisUrl, orredisOptions) to share the blocklist across all instances. - Updates: Feeds are refreshed periodically based on
threatIntelFetchIntervalMinutes. - Errors: Handle potential feed fetch/parse errors using the
onThreatIntelErrorcallback.
JA3 Fingerprinting
- Concept: JA3 analyzes the parameters of a client's TLS handshake to create a fingerprint. Certain fingerprints are strongly associated with malware or specific bot tools.
- CRITICAL: Penjaro cannot calculate the JA3 hash itself. You must have an upstream TLS-terminating proxy (like Nginx, Envoy, HAProxy, or Cloudflare) configured to:
- Calculate the JA3 hash for incoming connections.
- Inject that hash into an HTTP request header sent to Penjaro.
- Enable: Set
enableJa3Check: true. - Configuration:
- Ensure
ja3HeaderNamematches the header your upstream proxy is injecting (case-insensitive, defaultX-JA3-Hash). - Populate
badJa3Hasheswith known malicious fingerprints you want to block. Some common ones are included by default.
- Ensure
GraphQL Protection
- Enable: Set
enableGraphqlProtection: true. - Dependency: You must have the
graphqlpackage installed (npm install graphql). - Checks: Applies only to POST requests identified as GraphQL (typically checks for a
queryfield in the body).graphqlMaxDepth: Limits query nesting.graphqlMaxComplexity: Limits the number of requested fields (heuristic).graphqlBlockIntrospection: Blocks schema introspection queries (usually disabled in production).graphqlBlockKeywords: Checks string arguments/values for potentially malicious keywords.
Response Analysis (Proxy Mode Only)
- Enable: Controlled by
enableResponseAnalysis(master switch),enableDataLeakChecks, andenableVerboseErrorChecks. - Mechanism: Buffers the response body (up to
responseAnalysisMaxSizefor specifiedresponseAnalysisContentTypes) and runs regex checks. - Performance Impact: Buffering responses adds latency and memory usage, and breaks server-sent events (SSE) or other streaming responses. Disable
enableResponseAnalysisif you rely heavily on streaming. - Customization: Add your own
customDataLeakPatternsandcustomVerboseErrorPatterns. UsereplaceDefaultPatterns: trueto use only your custom patterns.
Redis Integration
- Purpose: Used to share state for Rate Limiting and Threat Intelligence blocklists across multiple Node.js instances of your application.
- Setup: Provide Redis connection details via one of these options (in order of precedence):
redisClient: A pre-configured, connectedredisv4 client instance.redisUrl: A Redis connection string.redisOptions: A Redis options object.
- Fallback: If Redis connection fails or is not configured, Rate Limiting and Threat Intel fall back to using in-memory stores (state is not shared between instances).
Incident Reporting
When Penjaro blocks a request or detects a security event (and enableIncidentReporting is true), it sends details about the incident (request headers, source IP, reason for block, etc.) to the Penjaro API endpoint associated with your apiKey. This allows you to monitor security events in your Penjaro dashboard.
API Key
The apiKey is required for Penjaro to validate its configuration and report incidents. Obtain your key from the Penjaro dashboard or your administrator. Keep your API key secure and prefer loading it from environment variables (process.env.PENJARO_API_KEY) rather than hardcoding it.
License
Currently UNLICENSED. Please contact the author for licensing inquiries.