npm package: @mitre/hdf-extension-graph
Description: Bidirectional extension graph processing for HDF profile/baseline hierarchies
Author: MITRE Corporation
Latest version: 2.0.0

# @mitre/hdf-extension-graph

Bidirectional extension graph processing for HDF baseline hierarchies. Builds a navigable graph from `HdfResults` files that reveals how baselines (profiles) and requirements (controls) relate across extension/overlay layers.

Ports the algorithm from Heimdall2's `context.ts` to the HDF v2 schema.

## Installation

```bash
pnpm add @mitre/hdf-extension-graph
```

Requires `@mitre/hdf-schema` as a peer dependency.

## Usage

### Build the graph

```typescript
import { buildExtensionGraph } from '@mitre/hdf-extension-graph';
import type { HdfResults } from '@mitre/hdf-schema';

const hdfResults: HdfResults = JSON.parse(fileContents);
const graph = buildExtensionGraph(hdfResults);
```

### Navigate baselines

```typescript
// Find root baselines (no parent)
const roots = graph.rootBaselines;

// Find a specific baseline
const stig = graph.findBaseline('rhel9-stig-baseline');

// See what extends it
for (const overlay of stig.extendedBy) {
  console.log(`${overlay.data.name} extends ${stig.data.name}`);
}
```

### Navigate requirements

```typescript
// Find all instances of a control across all baselines
const controls = graph.findRequirements('SV-238196');

// Get the root (base) control
const root = controls[1].root; // walks up the chain

// Get the full code with all layers
console.log(controls[1].fullCode);
// # my-overlay
// describe sshd_config do
//   its("ClientAliveInterval") { should cmp <= 300 }
// end
//
// # rhel9-stig-baseline
// describe sshd_config do
//   its("ClientAliveInterval") { should cmp <= 600 }
// end
```

### Detect changes

```typescript
const overlay = graph.baselines[1].requirements[0];

// Is this overlay just inheriting, or did it change something?
if (!overlay.isRedundant) {
  console.log('This overlay modifies the base control');
}

// What specifically changed?
for (const mod of overlay.modifications) {
  console.log(`${mod.field}: ${mod.originalValue} → ${mod.newValue}`);
}
// impact: 0.5 → 0.9
// title: SSH timeout → SSH timeout (project)
```

### Walk the chain

```typescript
// Ordered list of baselines from root to current
const chain = overlay.extensionChain;
console.log(chain.map(b => b.data.name));
// ['disa-rhel7-stig', 'cms-rhel7-overlay', 'project-overlay']
```

## API

### `buildExtensionGraph(results: HdfResults): ExtensionGraph`

Builds a bidirectional extension graph from an HDF Results file. Links baselines via `parentBaseline` and requirements by matching `id` across linked baselines.

### `ExtensionGraph`

| Property / Method | Type | Description |
|---|---|---|
| `baselines` | `ContextualizedBaseline[]` | All baselines in the graph |
| `requirements` | `ContextualizedRequirement[]` | All requirements across all baselines |
| `rootBaselines` | `ContextualizedBaseline[]` | Baselines with no parent |
| `findBaseline(name)` | `ContextualizedBaseline \| undefined` | Find baseline by name |
| `findRequirements(id)` | `ContextualizedRequirement[]` | Find all requirements with given id |

### `ContextualizedBaseline`

| Property | Type | Description |
|---|---|---|
| `data` | `EvaluatedBaseline` | Original baseline data |
| `sourcedFrom` | `HdfResults` | The results file this came from |
| `extendsFrom` | `ContextualizedBaseline[]` | Parent baselines |
| `extendedBy` | `ContextualizedBaseline[]` | Child baselines |
| `requirements` | `ContextualizedRequirement[]` | Wrapped requirements |

### `ContextualizedRequirement`

| Property | Type | Description |
|---|---|---|
| `data` | `EvaluatedRequirement` | Original requirement data |
| `sourcedFrom` | `ContextualizedBaseline` | Owning baseline |
| `extendsFrom` | `ContextualizedRequirement[]` | Parent requirements |
| `extendedBy` | `ContextualizedRequirement[]` | Child requirements |
| `root` | `ContextualizedRequirement` | Base requirement at bottom of chain |
| `isRedundant` | `boolean` | True if code is empty or matches root |
| `fullCode` | `string` | Concatenated code from all layers |
| `extensionChain` | `ContextualizedBaseline[]` | Baselines from root to leaf |
| `modifications` | `Modification[]` | Fields changed vs immediate parent |

### `Modification`

```typescript
interface Modification {
  field: string;        // 'impact', 'title', or 'severity'
  originalValue: unknown;
  newValue: unknown;
  inBaseline: string;   // Name of the baseline making the change
}
```

## License

Apache-2.0