
Security News
Open VSX Begins Implementing Pre-Publish Security Checks After Repeated Supply Chain Incidents
Following multiple malicious extension incidents, Open VSX outlines new safeguards designed to catch risky uploads earlier.
@trigo/atrix
Advanced tools
[](https://nodesecurity.io/orgs/trigo-gmbh/projects/6f4ad9d2-40fc-452b-8ae1-41433733d816)
Atrix is an opinionated micro-service framework
In the examples below, several
/config.jsfiles are cited. In a single project, you may have more than one config file, however, only one config file is used to create a service.
/demoService/handlers/{id}_GET.js
module.exports = (req, reply, service) => {
reply({ status: 'ok' });
}
/demoService/config.js
module.exports = {
// name of the service (REQUIRED)
name: 'demoService',
endpoints: {
http: {
// declare port to bind
port: 3007,
// the directory containing the handler files
handlerDir: `${__dirname}/handlers`,
// global server cors configuraton
// see: https://hapijs.com/api#route-options rules apply here too
cors: {
// defaults to '*'
origin: ['https://myui.myservice.at', 'http://lvh.me'],
// allow additional headers to be sent when client XHR
// lib is sending them like angular $http etc
additionalHeaders: ['x-requested-with']
},
// request logger configuration
requestLogger: {
// enable the request logger
enabled: false,
// log full request body if content-type: application/javascript and multipart/form-data
logFullRequest: true,
// log full response if content-type: application/javascript
logFullResponse: true,
},
// validation settings
validation: {
// list of regular expression that define the routed that should
// return structured and verbose validation error responses that
// may be used in the fronened form logic et al.
verbose: ['^/items$']
}
},
},
// Add service settings in here. They are accessible in the handler as "service.settings" object
settings: {
test: 'value',
},
};
/index.js
'use strict';
// get global atrix instance
const atrix = require('@trigo/atrix');
// load service config
const config = require('./demoService/config');
// crete service with config
const service = atrix.addService(config);
// start the service
atrix.services.demoService.start(); // returns promise
Declare a directory in which all handlers are contained, or add route handlers manually.
When using the handlerDir option the appropriate routes will be created based on the filenames and folder structure. The Caret symbol ^ is used as subroute indicator when using a single filename to represent a deep route. Route params can be defined by curly brackets e.g.: {id}
Special Characters:
_ the last underscore in the filename indicates the beginning of the http method to be used e.g.: persons_GET.js^ indicates the beginning of a subroute, e.g.: persons^details_GET.jspersons^{id}_GET.js;% by default) can be used to create a route for all http methods persons_%.jsExamples:
/handlers/persons^{id}^details_GET.js will create a route GET /persons/{id}/details./handlers/persons/{id}/details/GET.js will create the same route.%) can be used for the HTTP method file ending. A file with a wildcard character as a method would be open for following the HTTP methods: GET, PUT, POST, PATCH, OPTIONS, DELETECode Example:
/config.js
module.exports = {
name: 'dummyService', // mandatory property
endpoints: {
http: {
port: 3000,
// the directory containing the handler files
handlerDir: `${__dirname}/handlers`,
},
},
};
/handlers/persons_GET.js
module.exports = (req, reply, service) => {
reply({status: 'ok'});
};
The route GET /persons is made available by the above examples.
Once a service has been created and an endpoint has been added, routes can be added manually.
const atrix = require('@trigo/atrix');
const service = atrix.addService({
name: 'dummyService',
endpoints: {
http: {
port: 3000,
},
},
});
// service.handlers.add(httpMethod, route, handler);
service.handlers.add('GET', '/persons/{id}/details', (req, reply, service) => {
reply({status: 'ok'});
});
service.start();
Atrix uses a declarative pattern to define security options based on authentication strategies as they are implemented in Hapi.
Example config:
{
name: 'secureService',
// the security related settings
security: {
// define which strategies are available in your service
stragtegies: {
// JWT based authentication
jwt: {
// the jwt secret used to sign the tokens
secret: 'jwt-secret-key',
// the algorithm to use. Change to RS256 for priv/pubkey signing
algorithm: 'HS256'
},
// authentication baes on query param "auth" containing a vaild signatiure of the link
signedlink: {
// the singed link secret used to create the signature
secret: 'loink-sign-secret',
// override default behaviour when signed lonk authorization failes.
failAction: async (request, h, reason) => {
// use the "h" parameter to return custom responses see:
// https://hapijs.com/api#response-toolkit
// return HTTP 401 Unauthorized. This is the default implementation
// that is used when the failAction option is omited.
// to ignore the failure return h.continue
return h.unauthorized(Boom.unauthorized(reason))
}
},
// authenticate using HTTP Basic Auth
basic: {
// setup the function used to validate your user credentials
// if credentials are correct return an object of shape
// {
// isValid: true,
// // the credentials to attach to the auth context that are acessible in
// // the request handlers
// credentials: {...}
// }
// when authentication fails:
// { isValid: false }
// to return a standard Boom.unauthorized() error
validate: async (request, username, password) => {
// the actual authentication logic
const success = await myCustomUsernamePasswordValidator(username, password);
// credentials are ok
if (success) {
return {
// it worked
isValid: true,
// the credentials to attach to the auth context
credentials: { username, foo: 'bar' }
};
}
// authentication failed
return {isValid: false};
},
// if set true, empty usernames are allowed
allowEmptyUsername: false
}
},
// attach to enpoints using endpoint expressions
endpoints:{
// apply to everything below /secured-by-jwt
jwt: ['^/secureed-by-jwt.*'],
// apply to everything below /secured-by-signedlink
signedlink: ['^/secureed-by-signedlink.*'],
// apply basic authenication to /with-basic-auth and below
basic: ['^/with-basic-auth.*'],
}
}
}
Atrix uses Hapi/Joi to perform request and response validation.
Validation is configureed by configuing hapi's route.options.validate object https://hapijs.com/api#route-options.
Whenever possible use the atrix-swagger plugin to setup proper validations for your API.
As in some cases this will not be suitable for your needs (e.g. limitation of swagger et al) you can allways configure those options manually
service.handlers.add('POST', '/{id}', (req, reply) => reply(req.payload), {
validate: {
params: {
id: Joi.string().regex(/^[a-z]{3}$/),
},
},
});
handlers/cars/{id}/POST.js
const Joi = require('joi');
module.exports.options = {
validate: {
payload: Joi.object({
name: Joi.string().required(),
}),
},
params: {
id: Joi.string().required().regex(/[0-9a-f]{16}/)
},
query: <schema>
headers: <schema>
response: {
status: {
201: <schema>,
202: <schema>,
}
}
};
module.exports.handler = async (req, reply, service) => { ... };
The validation option are applied to the routes after all other configurations are done by route processor plugins like atrix-swagger et al.
/config.js
module.exports = {
name: 'serviceName',
endpoints: {
http: {
port: 3000,
// the validation config
validation: {
// list of route patterns of the routes that should return
// vaerbose validation errors
// defaults to: []
verboseEndpoints: ['^/internal/.*$', ...]
// list of route patterns that enforce strict validation. E.g. do not
// allow unknown keys. When strict checking is disabled the unknown
// keys will be ignored and stripped from the objects before they are
// passed on the header.
strictEndpoints: ['^/public.*$']
},
},
},
};
Per default the server reutrns just HTTP statusCode 400 Bad Request withpout any further details where exactly the validation failed.
{
"statusCode": 400,
"error": "Bad Request",
"message": "Invalid request payload input"
}
When enabling verbose validation the errors response contains details aboout all failed validators, thier types and expected/valid values.
{
"statusCode": 400,
"error": "Bad Request",
"message": "child \"events\" fails because [\"events\" at position 1 fails because [child \"resId\" fails because [\"resId\" is required]]]. child \"links\" fails because [child \"href\" fails because [\"href\" must be a valid uri], child \"method\" fails because [\"method\" must be one of [GET, POST, PUT]], child \"response\" fails because [\"response\" with value \"herbert\" fails to match the required pattern: /^testOida$/]]",
"validation": {
"source": "payload",
"keys": [
"events.1.resId",
"links.href",
"links.method",
"links.response"
]
},
"details": [
{
"message": "\"resId\" is required",
"path": [ "events", 1, "resId" ],
"type": "any.required",
"context": {
"key": "resId",
"label": "resId"
}
},
{
"message": "\"href\" must be a valid uri",
"path": [ "links", "href" ],
"type": "string.uri",
"context": {
"value": "asdf",
"key": "href",
"label": "href"
}
},
{
"message": "\"method\" must be one of [GET, POST, PUT]",
"path": [ "links", "method" ],
"type": "any.allowOnly",
"context": {
"value": "franz",
"valids": [ "GET", "POST", "PUT" ],
"key": "method",
"label": "method"
}
},
{
"message": "\"response\" with value \"herbert\" fails to match the required pattern: /^testOida$/",
"path": [ "links", "response" ],
"type": "string.regex.base",
"context": {
"pattern": "/^testOida$/",
"value": "herbert",
"key": "response",
"label": "response"
}
}
]
}
The detailed documentation about the possible errors, their properties and options see: https://github.com/hapijs/joi/blob/v14.3.0/API.md#list-of-errors
/config.js
module.exports = {
name: 'serviceName',
endpoints: {
http: {
port: 3000,
// global server cors configuraton
// see: https://hapijs.com/api#route-options rules apply here too
cors: {
// defaults to '*'
origin: ['https://myui.myservice.at', 'http://lvh.me'],
// allow additional headers to be sent when client XHR
// lib is sending them like angular $http etc
additionalHeaders: ['x-requested-with']
},
},
},
};
/config.js
module.exports = {
name: 'serviceName',
endpoints: {
http: {
port: 3000,
// request logger configuration
requestLogger: {
// enable the request logger
enabled: false,
// log full request body if content-type: application/javascript and multipart/form-data
logFullRequest: true,
// log full response if content-type: application/javascript
logFullResponse: true,
},
},
},
};
The atrix logger uses bunyan under the hood. For more info about bunyan streams have a look at the bunyan stream documentation.
/config.js
module.exports = {
name: 'serviceName',
logger: {
level: 'debug',
name: 'dummyDebugger', // optional, atrix would insert the services name if no logger name is provided
streams: [], // optional, bunyan streams
}
}
The logger can be accessed on the request object in every service handler.
/simple_GET.js
module.exports = (req, reply) => {
req.log.debug('I am a debug message');
req.log.info('I am an info message');
req.log.warn('I am a warning message');
req.log.info('I am a error message');
reply({status: 'ok'});
};
Optionally, you can also access the logger of your service as it is exposed via atrix:
/service.js
const atrix = require('@trigo/atrix');
const service = atrix.addService({.name: 'dummyService', ...service configuration...});
// access directly using service instance
service.log.info('I am the dummyService logger');
// access through atrix
atrix.service.dummyService.log.info('I am also the dummyService logger');
Add service settings in here. They are accessible in the handler as the "service.settings" object
/config.js
module.exports = {
settings: {
pika: 'chu',
},
};
For example, if the service were to be called demoService, you could access its settings like this:
const atrix = ('@trigo/atrix');
const service = atrix.services.demoService;
const pikaValue = service.settings.pika;
Or in every service handler
module.exports = (req, res, service) => {
req.log.info(`Value of Pika is ${service.settings.pika}`);
}
Atrix uses axios for HTTP requests and can be configured for multiple upstreams. Upstreams will expose a simple interface to make preconfigured HTTP requests.
Example of a basic upstream configuration /config.js
module.exports = {
upstream: {
example: {
url: 'http://some.url',
},
},
};
The defined upstream can be accessed in every service handler via the service parameter.
Example usage of upstream directly inside a service handler /simple_GET.js
module.exports = async (req, reply, service) => {
const result = await service.upstream.example.get('/');
req.log.info(result);
reply({status: 'ok'});
};
Alternativ the upstreams can be accessed via the exposed service from atrix.
Example usage of upstream inside a module which is called from within the dummyService /some_file/which_is/part_of/the_service
const atrix = require('@trigo/atrix');
// here we assume the service has been named 'dummyService'
const service = atrix.dummyService;
const exampleUpstream = service.upstream.example;
You can define options (e.g.: headers) which will be merged into the underlying fetch request.
Example configuration for upstream headers /config.js
module.exports = {
upstream: {
example: {
url: 'http://some.url',
options: {
headers: {
'User-Agent': 'ATRIX_SERVICE',
},
},
},
},
};
Upstreams can be configured to automatically retry the requests in case of an error for several times with a defined interval.
Example configuration for retry upstream /config.js
module.exports = {
upstream: {
example: {
url: 'http://some.url',
retry: {
interval: 1000, // milliseconds
max_tries: 3,
},
},
},
};
You can set up basic authentication or oAuth authentication which will be handled by the upstream itself.
Example configuration for a basic authentication upstream /config.js
module.exports = {
upstream: {
example: {
url: 'http://some.url',
security: {
strategies: {
basic: {
username: 'username',
password: 'password',
},
},
},
},
},
};
The OAuth strategy will try to authenticate against the provided authEndpoint and grantType via Basic authentication. The auth endpoint has to return a JSON answer contiaining an access_token.
// json answer e.g.:
{
access_token: '123456'
}
After the initial configuration it is not necessary to authenticate manually - upstream will handle the authentication process on the first request and will cache the returning access_token for further requests.
Example configuration for a oauth authentication upstram /config.js
module.exports = {
upstream: {
example: {
url: 'http://some.url',
security: {
strategies: {
oauth: {
clientId: 'client_id',
clientSecret: 'client_secret',
authEndpoint: 'http://auth.endpoint/token',
grantType: 'password',
},
},
},
},
},
};
Every variable defined in the /config.js can be overwritten by declaring environment variables. Configurations that are not already defined in /config.js may not be declared by environment variables - especially arrays - you may not insert additional items to arrays...
They have to follow a strict pattern. The environment variable has to be defined in snakecased uppercased words eg. THIS_IS_AN_ENV_VAR. Starting with ATRIX, followed by the atrix service's name, which is defined by the new atrix.Service('demoService', config) call. For the demoService we would have to start with ATRIX_DEMOSERVICE_ as environment variable name.
/config.js
module.exports = {
settings: {
nestedSetting: {
pika: 'chu',
},
},
};
To overwrite the value of pika we would have to define the env variable like that:
ATRIX_DEMOSERVICE_SETTINGS_NESTEDSETTING_PIKA=chuchu
FAQs
[](https://nodesecurity.io/orgs/trigo-gmbh/projects/6f4ad9d2-40fc-452b-8ae1-41433733d816)
The npm package @trigo/atrix receives a total of 150 weekly downloads. As such, @trigo/atrix popularity was classified as not popular.
We found that @trigo/atrix demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Following multiple malicious extension incidents, Open VSX outlines new safeguards designed to catch risky uploads earlier.

Research
/Security News
Threat actors compromised four oorzc Open VSX extensions with more than 22,000 downloads, pushing malicious versions that install a staged loader, evade Russian-locale systems, pull C2 from Solana memos, and steal macOS credentials and wallets.

Security News
Lodash 4.17.23 marks a security reset, with maintainers rebuilding governance and infrastructure to support long-term, sustainable maintenance.