loopbackio / loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.
https://loopback.io
Other
4.93k stars 1.06k forks source link

Add auditor extension point #6402

Open achrinza opened 4 years ago

achrinza commented 4 years ago

Table of Contents

Suggestion

Add an extension point to accept an Auditor.

The Auditor can take up 2 auditing roles, Static and Dynamic, and may adopt the following interface(s):

import {LifeCycleObserver} from '@loopback/core';

/**
 * Used by `Auditors` to create both static and dynamic audit entries to be
 * passed to `@loopback/core`.
 */
export interface BaseAuditEntry {
  /**
   * A short, recognisable string to identify the incident.
   */
  title: string;

  /**
   * A longer descriptive string to explain the incident.
   */
  description: string;
}

/**
 * Must only be used by `@loopback/core` to generate static audit entries from
 * `BaseAuditEntry`.
 */
export interface StaticAuditEntry extends BaseAuditEntry {}

/**
 * Must only be used by `@loopback/core` to generate dynamic audit entries from
 * `BaseAuditEntry`.
 */
export interface DynamicAuditEntry extends BaseAuditEntry {
  /**
   * The time of which the audit entry was received.
   */
  timestamp: Date;
}

export interface AuditorConfig {
  rules: AuditorRule[];
}

/**
 * A rule to dictate the action to be taken (and optionally, the threshold)
 *
 * @remarks
 * - 'error' indicates that the application should terminate immediately. This
 * is useful to ensure that the application returns to a safe state.
 * - 'warning' indicates that the application should log the error, but not take
 * any other action. This is useful for testing new rules on an existing
 * application.
 * - _an integer_ - indicates that the application should only log the incident
 * _x_ times, then immediately terminate after the next incident.
 */
export interface AuditorRule {
  [key: string]: 'error' | 'warning' | 'off' | number;
}

/**
 * The base interface for `StaticAuditor` and `DynamicAuditor`
 *
 * @remarks
 * This **must not** be used anywhere else beyond as the common base of
 * `StaticAudtor` and `DynamicAuditor`.
 *
 * @internal
 */
export interface BaseAuditor {
  /**
   * The display name of the auditor, solely used for identifying
   * and differentiating between auditors in logs. It _should_ be unique, but name
   * collisions will not break the program.
   */
  name: string;
}

/**
 * An auditor that only performs analysis after initial application start.
 */
export interface StaticAuditor extends BaseAuditor {
  run(): BaseAuditEntry[];
}

/**
 * An auditor that only performs analysis based on abitrary triggers and hooks
 * throughout the lifecycle of the application instance.
 */
export interface DynamicAuditor extends BaseAuditor, LifeCycleObserver {}

Use Cases

LoopBack 4 is designed as a framework to build a wide variety of applications. This means opportunities for the developers' hardening routines to miss potentially-important areas of security concern.

There are 3 main user stories:

Overall, these users stories share the need to increase discoverability. By introducing a common audit framework, we would be able to shift more of the effort on to the framework and extension developers instead.

The above implementation idea caters to all 3 user stories by providing a simple, common interface that can be adopted.

Examples

This is more of an implementation idea, less of a concrete example:

From the perspective of a developer, I do not need to do anything beyond creating a AuditorConfig to inform LoopBack 4 of how to enforce the rules. This is similar to eslint rules.

A Component should be able to register new auditors with an auditors property.

The core framework should automatically register new auditors.

Auditors should be off by default, but the application should log a warning if an AuditorConfig does not exist.

To manually register an auditor in the Application constructor:

class SampleAuditor implements StaticAuditor {
  constructor(
    // Dependency injection here
  );
  name = "Sample Auditor"
  run() {
    // Run static audits.
  }
}

// ...

this.auditor(SampleAuditor);

Additional Expansion: Security Content Automation Protocol (SCAP) v1

SCAP v1 is an open standard created by the National Institute of Standards and Technology (NIST) that is a suite of specifications that standardize the format and nomenclature by which software flaw and security configuration information is communicated, both to machines and humans.

The LoopBack 4 auditor extension point is an ideal candidate for adopting SCAP v1 as:

  1. Auditors are able to identify and report mis-configuration and vulnerabilities
  2. Abstraction of SCAP v1 from auditors enables more robust auditor development
  3. Unified SCAP v1 codebase enables tidier and more robust developer code.

The @loopback/core package's auditor extension point can expose a customizable SCAP v1 implementation. This would enable developers to quickly integrate the enabled auditors with their security solution without needing a third-party scanner or custom code.

Acceptance criteria

TBD - will be filled by the team.

dhmlau commented 3 years ago

@achrinza, thanks for the suggestion. I'm thinking whether it would fit better as a community extension.

@raymondfeng @bajtos, WDYT?

achrinza commented 3 years ago

I'm leaning towards making this a built-in extension point for 2 reasons:

  1. Audits should be a well-defined interface adopted by the core lb4 packages
  2. The proposal is minimal and does not include the auditor implementation itself. Hence, there shouldn't be much additional baggage beyond lifecycle management and interface definitons.

WDYT?

stale[bot] commented 3 years ago

This issue has been marked stale because it has not seen activity within six months. If you believe this to be in error, please contact one of the code owners, listed in the CODEOWNERS file at the top-level of this repository. This issue will be closed within 30 days of being stale.