Addons Scanner Utilities

raw JSON →
15.0.0 verified Sun Apr 19 auth: no javascript

The `addons-scanner-utils` library provides a collection of helper functions specifically designed for building command-line interfaces (CLIs) and backend services within the Mozilla add-ons scanning ecosystem. Currently at version `15.0.0`, it maintains a rapid release cadence, with several major versions released in quick succession, indicating active and continuous development. Key functionalities include robust error handling through the `AppError` class, secure JSON Web Token (JWT) generation for authenticating with platforms like Add-ons for Mozilla (AMO), and versatile file operations such as downloading remote resources. The library is often integrated with `express` for API development, supporting specific authentication patterns like `HMAC-SHA256` and handling `X-Forwarded-Authorization` headers. Its core differentiator is its specialized focus on the Mozilla add-ons context, offering tailored solutions for common operational tasks in that domain. It ships with comprehensive TypeScript types, ensuring type-safe development.

error ReferenceError: ApiError is not defined
cause The `ApiError` class was renamed to `AppError` in version 14.0.0.
fix
Replace all occurrences of ApiError with AppError in your code, including import statements.
error Error: This module requires Node.js version 22 or higher.
cause The application is running in an environment with an outdated Node.js version.
fix
Update your Node.js runtime to version 22 or a later compatible version.
error TypeError: (0 , addons_scanner_utils_1.makeJWT) is not a function
cause Attempting to use CommonJS `require()` syntax to import ES module named exports.
fix
Refactor your import statements to use ES module syntax: import { makeJWT } from 'addons-scanner-utils'; and ensure your project supports ESM.
error Unauthorized: Bearer token missing or malformed
cause The incoming request is missing the `Authorization` header, or its `Bearer` token format is incorrect, or the library's internal auth logic no longer supports this method (v15).
fix
Ensure the client sends a correctly formatted Authorization: Bearer YOUR_TOKEN header. If using the library's internal auth (pre-v15), verify configuration. For v15+, implement custom Bearer token validation logic.
breaking The `ApiError` class was renamed to `AppError`. Direct references to `ApiError` will cause runtime errors.
fix Update all import statements and class instantiations from `ApiError` to `AppError`.
gotcha This library now requires Node.js version 22 or higher to run. Older Node.js versions are no longer supported.
fix Upgrade your Node.js runtime environment to version 22 or newer to ensure compatibility.
breaking The internal authentication layer, if used, now strictly requires credentials to be passed via an `Authorization` HTTP header instead of a request body parameter.
fix Modify client-side applications to send authentication tokens exclusively within the `Authorization` HTTP header.
breaking Support for `Bearer` token based authentication within the library's built-in utilities has been dropped. If your application relied on the library's direct handling of Bearer tokens, this functionality must now be implemented manually.
fix Review and update any existing authentication logic that utilized the library's `Bearer` token utilities. Implement custom authentication middleware or handlers for processing Bearer tokens if necessary.
breaking The built-in Express handler for automatically downloading XPI (Mozilla addon) files has been removed. Applications previously relying on this functionality will need to implement custom file download logic.
fix Manually implement XPI file download logic using a custom Express route and potentially the `downloadFile` utility function provided by the library (introduced in v13.1.0).
npm install addons-scanner-utils
yarn add addons-scanner-utils
pnpm add addons-scanner-utils

This quickstart demonstrates a basic Express.js application integrating `addons-scanner-utils` for error handling (`AppError`), JWT generation (`makeJWT`), file downloading (`downloadFile`), and custom authentication logic using `safe-compare`.

import express, { Request, Response, NextFunction } from 'express';
import { AppError, makeJWT, downloadFile } from 'addons-scanner-utils';
import safeCompare from 'safe-compare'; // from peer dependency

const app = express();
const port = 3000;

// A placeholder for a secret key for HMAC-SHA256 or JWT signing
const JWT_SECRET = process.env.JWT_SECRET ?? 'super_secret_jwt_key_please_change';
const STATIC_AUTH_TOKEN = process.env.STATIC_AUTH_TOKEN ?? 'my-secure-token-123_please_change';

// Middleware to simulate an authentication check (e.g., Bearer token)
function authMiddleware(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return next(new AppError('Unauthorized: Bearer token missing or malformed', 401));
  }
  const token = authHeader.split(' ')[1];
  // In a real application, validate JWT or compare with a stored secret securely.
  // Using safeCompare for demonstration with a static token.
  if (!safeCompare(token, STATIC_AUTH_TOKEN)) {
    return next(new AppError('Unauthorized: Invalid token', 401));
  }
  next();
}

app.get('/', (req: Request, res: Response) => {
  res.send('Addons Scanner Utils Example API');
});

app.get('/protected', authMiddleware, (req: Request, res: Response) => {
  res.json({ message: 'Access granted to protected resource.' });
});

app.get('/jwt', (req: Request, res: Response, next: NextFunction) => {
  try {
    const issuer = 'your-service';
    const expiresInMinutes = 5;
    // In a production environment, ensure JWT_SECRET is a strong, securely stored key.
    const jwtToken = makeJWT(issuer, JWT_SECRET, expiresInMinutes);
    res.json({ jwt: jwtToken, message: `JWT created for ${issuer}, valid for ${expiresInMinutes} minutes.` });
  } catch (error) {
    next(new AppError('Failed to create JWT', 500, error as Error));
  }
});

app.get('/download-example', async (req: Request, res: Response, next: NextFunction) => {
  const fileUrl = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png'; // Example URL
  const destinationPath = '/tmp/downloaded-image.png'; // Ensure /tmp is writable or change path

  try {
    await downloadFile(fileUrl, destinationPath);
    res.json({ message: `File downloaded successfully to ${destinationPath}` });
  } catch (error) {
    next(new AppError('Failed to download file', 500, error as Error));
  }
});

// Error handling middleware
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  if (err instanceof AppError) {
    console.error(`AppError: ${err.message}`, err.originalError);
    res.status(err.statusCode).json({
      error: err.name,
      message: err.message
    });
  } else {
    console.error('Unhandled error:', err);
    res.status(500).json({
      error: 'InternalServerError',
      message: 'An unexpected error occurred.'
    });
  }
});

app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});