cwe-api-client 0.0.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -0
- package/dist/index.cjs +382 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +594 -0
- package/dist/index.d.ts +594 -0
- package/dist/index.js +359 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -9
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# cwe-api-client
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ElJijuna/cwe-api-client/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/cwe-api-client)
|
|
5
|
+
[](https://bundlephobia.com/package/cwe-api-client)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
|
|
10
|
+
TypeScript client for the [MITRE CWE REST API](https://cwe-api.mitre.org/api/v1).
|
|
11
|
+
Works in **Node.js** and the **browser** (isomorphic). Fully typed, zero runtime dependencies.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install cwe-api-client
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { CweClient } from 'cwe-api-client';
|
|
27
|
+
|
|
28
|
+
const cwe = new CweClient();
|
|
29
|
+
|
|
30
|
+
// Custom API base URL (e.g. a mirror)
|
|
31
|
+
const cwe = new CweClient({ baseUrl: 'https://my-mirror.example.com/api/v1' });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## API reference
|
|
37
|
+
|
|
38
|
+
### Version metadata
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const version = await cwe.version();
|
|
42
|
+
|
|
43
|
+
console.log(version.ContentVersion); // '4.19.1'
|
|
44
|
+
console.log(version.ContentDate); // '2026-01-21'
|
|
45
|
+
console.log(version.TotalWeaknesses); // 969
|
|
46
|
+
console.log(version.TotalCategories); // 420
|
|
47
|
+
console.log(version.TotalViews); // 58
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Multi-ID lookup
|
|
51
|
+
|
|
52
|
+
Look up multiple CWE entries by their numeric IDs in a single request:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const entries = await cwe.lookup([74, 79]);
|
|
56
|
+
|
|
57
|
+
entries.forEach(e => console.log(`CWE-${e.ID} (${e.Type})`));
|
|
58
|
+
// CWE-74 (class_weakness)
|
|
59
|
+
// CWE-79 (base_weakness)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Weakness details
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Await directly
|
|
66
|
+
const weakness = await cwe.weakness(79);
|
|
67
|
+
|
|
68
|
+
// Or call .get() explicitly
|
|
69
|
+
const weakness = await cwe.weakness(79).get();
|
|
70
|
+
|
|
71
|
+
console.log(weakness.Name); // 'Improper Neutralization of Input...'
|
|
72
|
+
console.log(weakness.Abstraction); // 'Base'
|
|
73
|
+
console.log(weakness.Status); // 'Stable'
|
|
74
|
+
console.log(weakness.LikelihoodOfExploit); // 'High'
|
|
75
|
+
console.log(weakness.Description);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Category details
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const category = await cwe.category(189);
|
|
82
|
+
|
|
83
|
+
console.log(category.Name); // 'Numeric Errors'
|
|
84
|
+
console.log(category.Status); // 'Draft'
|
|
85
|
+
console.log(category.Summary);
|
|
86
|
+
console.log(category.Relationships?.length); // number of member weaknesses
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### View details
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const view = await cwe.view(1425);
|
|
93
|
+
|
|
94
|
+
console.log(view.Name); // 'Weaknesses in the 2023 CWE Top 25...'
|
|
95
|
+
console.log(view.Type); // 'Graph'
|
|
96
|
+
console.log(view.Status); // 'Draft'
|
|
97
|
+
console.log(view.Members?.length); // 25
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Hierarchy navigation
|
|
101
|
+
|
|
102
|
+
All hierarchy methods accept an optional `viewId` to scope the results to a specific CWE view (e.g. `1000` for Research Concepts):
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Direct parents
|
|
106
|
+
const parents = await cwe.weakness(74).parents(1000);
|
|
107
|
+
parents.forEach(p => console.log(`CWE-${p.ID}`, p.Primary_Parent ? '[primary]' : ''));
|
|
108
|
+
|
|
109
|
+
// Direct children
|
|
110
|
+
const children = await cwe.weakness(74).children(1000);
|
|
111
|
+
children.forEach(c => console.log(`CWE-${c.ID} (${c.Type})`));
|
|
112
|
+
|
|
113
|
+
// Full ancestor tree (recursive, up to the view root)
|
|
114
|
+
const ancestors = await cwe.weakness(74).ancestors(1000);
|
|
115
|
+
// ancestors[0].Data → this node
|
|
116
|
+
// ancestors[0].Parents → parent nodes (recursive)
|
|
117
|
+
|
|
118
|
+
// Full descendant tree (recursive, down to leaf entries)
|
|
119
|
+
const descendants = await cwe.weakness(74).descendants(1000);
|
|
120
|
+
// descendants[0].Data → this node
|
|
121
|
+
// descendants[0].Children → child nodes (recursive)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Chainable resource pattern
|
|
127
|
+
|
|
128
|
+
Every resource implements `PromiseLike`, so it can be **awaited directly** or **chained** to sub-methods:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// Await directly → fetches the full weakness
|
|
132
|
+
const weakness = await cwe.weakness(79);
|
|
133
|
+
|
|
134
|
+
// Chain → fetches hierarchy
|
|
135
|
+
const parents = await cwe.weakness(74).parents(1000);
|
|
136
|
+
const descendants = await cwe.weakness(74).descendants(1000);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Request events
|
|
142
|
+
|
|
143
|
+
Subscribe to every HTTP request for logging, monitoring, or debugging:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
cwe.on('request', (event) => {
|
|
147
|
+
console.log(`[${event.method}] ${event.url} → ${event.statusCode} (${event.durationMs}ms)`);
|
|
148
|
+
if (event.error) {
|
|
149
|
+
console.error('Request failed:', event.error.message);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The `event` object contains:
|
|
155
|
+
|
|
156
|
+
| Field | Type | Description |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `url` | `string` | Full URL that was requested |
|
|
159
|
+
| `method` | `'GET'` | HTTP method used |
|
|
160
|
+
| `startedAt` | `Date` | When the request started |
|
|
161
|
+
| `finishedAt` | `Date` | When the request finished |
|
|
162
|
+
| `durationMs` | `number` | Duration in milliseconds |
|
|
163
|
+
| `statusCode` | `number \| undefined` | HTTP status code, if a response was received |
|
|
164
|
+
| `error` | `Error \| undefined` | Present only if the request failed |
|
|
165
|
+
|
|
166
|
+
Multiple listeners can be registered. The event is always emitted after the request completes, whether it succeeded or failed.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Error handling
|
|
171
|
+
|
|
172
|
+
Non-2xx responses throw a `CweApiError` with the HTTP status code and status text:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { CweApiError } from 'cwe-api-client';
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
await cwe.weakness(99999).get();
|
|
179
|
+
} catch (err) {
|
|
180
|
+
if (err instanceof CweApiError) {
|
|
181
|
+
console.log(err.status); // 404
|
|
182
|
+
console.log(err.statusText); // 'Not Found'
|
|
183
|
+
console.log(err.message); // 'CWE API error: 404 Not Found'
|
|
184
|
+
console.log(err.stack); // full stack trace
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## TypeScript types
|
|
192
|
+
|
|
193
|
+
All domain types are exported:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import type {
|
|
197
|
+
// Client
|
|
198
|
+
CweClientOptions, RequestEvent, CweClientEvents,
|
|
199
|
+
// Version
|
|
200
|
+
CweVersion,
|
|
201
|
+
// Multi-ID lookup
|
|
202
|
+
CweEntry, CweEntryType,
|
|
203
|
+
// Weakness
|
|
204
|
+
CweWeakness, CweRelatedWeakness, CweWeaknessOrdinality,
|
|
205
|
+
CweApplicablePlatform, CweAlternateTerm,
|
|
206
|
+
// Category
|
|
207
|
+
CweCategory, CweTaxonomyMapping, CweCategoryRelationship, CweCategoryReference,
|
|
208
|
+
// View
|
|
209
|
+
CweView, CweViewAudience, CweViewMember,
|
|
210
|
+
// Relations / hierarchy
|
|
211
|
+
CweRelationEntry, CweAncestorNode, CweDescendantNode,
|
|
212
|
+
} from 'cwe-api-client';
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Contributing
|
|
218
|
+
|
|
219
|
+
See [CONTRIBUTING.md](.github/CONTRIBUTING.md).
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
[MIT](LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CategoryResource: () => CategoryResource,
|
|
24
|
+
CweApiError: () => CweApiError,
|
|
25
|
+
CweClient: () => CweClient,
|
|
26
|
+
ViewResource: () => ViewResource,
|
|
27
|
+
WeaknessResource: () => WeaknessResource
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/errors/CweApiError.ts
|
|
32
|
+
var CweApiError = class extends Error {
|
|
33
|
+
constructor(status, statusText) {
|
|
34
|
+
super(`CWE API error: ${status} ${statusText}`);
|
|
35
|
+
this.name = "CweApiError";
|
|
36
|
+
this.status = status;
|
|
37
|
+
this.statusText = statusText;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/resources/WeaknessResource.ts
|
|
42
|
+
var WeaknessResource = class {
|
|
43
|
+
/** @internal */
|
|
44
|
+
constructor(request, id) {
|
|
45
|
+
this.request = request;
|
|
46
|
+
this.id = id;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Allows the resource to be awaited directly, resolving with the full weakness.
|
|
50
|
+
* Delegates to {@link WeaknessResource.get}.
|
|
51
|
+
*/
|
|
52
|
+
then(onfulfilled, onrejected) {
|
|
53
|
+
return this.get().then(onfulfilled, onrejected);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Fetches the full weakness entry.
|
|
57
|
+
*
|
|
58
|
+
* `GET /cwe/weakness/{id}`
|
|
59
|
+
*
|
|
60
|
+
* @returns The weakness object
|
|
61
|
+
*/
|
|
62
|
+
async get() {
|
|
63
|
+
const data = await this.request(`/cwe/weakness/${this.id}`);
|
|
64
|
+
return data.Weaknesses[0];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fetches the direct parents of this weakness in a given view.
|
|
68
|
+
*
|
|
69
|
+
* `GET /cwe/{id}/parents?view={viewId}`
|
|
70
|
+
*
|
|
71
|
+
* @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
|
|
72
|
+
* @returns Array of direct parent entries
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const parents = await cwe.weakness(74).parents(1000);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
async parents(viewId) {
|
|
80
|
+
return this.request(
|
|
81
|
+
`/cwe/${this.id}/parents`,
|
|
82
|
+
viewId !== void 0 ? { view: viewId } : void 0
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Fetches the direct children of this weakness in a given view.
|
|
87
|
+
*
|
|
88
|
+
* `GET /cwe/{id}/children?view={viewId}`
|
|
89
|
+
*
|
|
90
|
+
* @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
|
|
91
|
+
* @returns Array of direct child entries
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const children = await cwe.weakness(74).children(1000);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
async children(viewId) {
|
|
99
|
+
return this.request(
|
|
100
|
+
`/cwe/${this.id}/children`,
|
|
101
|
+
viewId !== void 0 ? { view: viewId } : void 0
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Fetches the full ancestor tree of this weakness in a given view.
|
|
106
|
+
*
|
|
107
|
+
* `GET /cwe/{id}/ancestors?view={viewId}`
|
|
108
|
+
*
|
|
109
|
+
* @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
|
|
110
|
+
* @returns Recursive ancestor tree from this node up to the view root
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const tree = await cwe.weakness(74).ancestors(1000);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
async ancestors(viewId) {
|
|
118
|
+
return this.request(
|
|
119
|
+
`/cwe/${this.id}/ancestors`,
|
|
120
|
+
viewId !== void 0 ? { view: viewId } : void 0
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Fetches the full descendant tree of this weakness in a given view.
|
|
125
|
+
*
|
|
126
|
+
* `GET /cwe/{id}/descendants?view={viewId}`
|
|
127
|
+
*
|
|
128
|
+
* @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)
|
|
129
|
+
* @returns Recursive descendant tree from this node down to leaf entries
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const tree = await cwe.weakness(74).descendants(1000);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
async descendants(viewId) {
|
|
137
|
+
return this.request(
|
|
138
|
+
`/cwe/${this.id}/descendants`,
|
|
139
|
+
viewId !== void 0 ? { view: viewId } : void 0
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/resources/CategoryResource.ts
|
|
145
|
+
var CategoryResource = class {
|
|
146
|
+
/** @internal */
|
|
147
|
+
constructor(request, id) {
|
|
148
|
+
this.request = request;
|
|
149
|
+
this.id = id;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Allows the resource to be awaited directly, resolving with the full category.
|
|
153
|
+
* Delegates to {@link CategoryResource.get}.
|
|
154
|
+
*/
|
|
155
|
+
then(onfulfilled, onrejected) {
|
|
156
|
+
return this.get().then(onfulfilled, onrejected);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Fetches the full category entry.
|
|
160
|
+
*
|
|
161
|
+
* `GET /cwe/category/{id}`
|
|
162
|
+
*
|
|
163
|
+
* @returns The category object
|
|
164
|
+
*/
|
|
165
|
+
async get() {
|
|
166
|
+
const data = await this.request(`/cwe/category/${this.id}`);
|
|
167
|
+
return data.Categories[0];
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/resources/ViewResource.ts
|
|
172
|
+
var ViewResource = class {
|
|
173
|
+
/** @internal */
|
|
174
|
+
constructor(request, id) {
|
|
175
|
+
this.request = request;
|
|
176
|
+
this.id = id;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Allows the resource to be awaited directly, resolving with the full view.
|
|
180
|
+
* Delegates to {@link ViewResource.get}.
|
|
181
|
+
*/
|
|
182
|
+
then(onfulfilled, onrejected) {
|
|
183
|
+
return this.get().then(onfulfilled, onrejected);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Fetches the full view entry.
|
|
187
|
+
*
|
|
188
|
+
* `GET /cwe/view/{id}`
|
|
189
|
+
*
|
|
190
|
+
* @returns The view object
|
|
191
|
+
*/
|
|
192
|
+
async get() {
|
|
193
|
+
const data = await this.request(`/cwe/view/${this.id}`);
|
|
194
|
+
return data.Views[0];
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// src/CweClient.ts
|
|
199
|
+
var DEFAULT_BASE_URL = "https://cwe-api.mitre.org/api/v1";
|
|
200
|
+
var CweClient = class {
|
|
201
|
+
/**
|
|
202
|
+
* @param options - Optional configuration for the API base URL
|
|
203
|
+
*/
|
|
204
|
+
constructor(options = {}) {
|
|
205
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
206
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Subscribes to a client event.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```typescript
|
|
213
|
+
* cwe.on('request', (event) => {
|
|
214
|
+
* console.log(`${event.method} ${event.url} — ${event.durationMs}ms`);
|
|
215
|
+
* if (event.error) console.error('Request failed:', event.error);
|
|
216
|
+
* });
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
on(event, callback) {
|
|
220
|
+
const callbacks = this.listeners.get(event) ?? [];
|
|
221
|
+
callbacks.push(callback);
|
|
222
|
+
this.listeners.set(event, callbacks);
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
emit(event, payload) {
|
|
226
|
+
const callbacks = this.listeners.get(event) ?? [];
|
|
227
|
+
for (const cb of callbacks) {
|
|
228
|
+
cb(payload);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Performs a GET request to the CWE API.
|
|
233
|
+
*
|
|
234
|
+
* @param path - Path to append to the base URL (e.g. `/cwe/version`)
|
|
235
|
+
* @param params - Optional query parameters
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
async request(path, params) {
|
|
239
|
+
const url = buildUrl(`${this.baseUrl}${path}`, params);
|
|
240
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
241
|
+
let statusCode;
|
|
242
|
+
try {
|
|
243
|
+
const response = await fetch(url, {
|
|
244
|
+
headers: { Accept: "application/json" }
|
|
245
|
+
});
|
|
246
|
+
statusCode = response.status;
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
throw new CweApiError(response.status, response.statusText);
|
|
249
|
+
}
|
|
250
|
+
const data = await response.json();
|
|
251
|
+
this.emit("request", {
|
|
252
|
+
url,
|
|
253
|
+
method: "GET",
|
|
254
|
+
startedAt,
|
|
255
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
256
|
+
durationMs: Date.now() - startedAt.getTime(),
|
|
257
|
+
statusCode
|
|
258
|
+
});
|
|
259
|
+
return data;
|
|
260
|
+
} catch (err) {
|
|
261
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
262
|
+
this.emit("request", {
|
|
263
|
+
url,
|
|
264
|
+
method: "GET",
|
|
265
|
+
startedAt,
|
|
266
|
+
finishedAt,
|
|
267
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
268
|
+
statusCode,
|
|
269
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
270
|
+
});
|
|
271
|
+
throw err;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Fetches CWE content version metadata.
|
|
276
|
+
*
|
|
277
|
+
* `GET /cwe/version`
|
|
278
|
+
*
|
|
279
|
+
* @returns Version metadata including content version, date, and counts
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const v = await cwe.version();
|
|
284
|
+
* console.log(v.ContentVersion); // '4.19.1'
|
|
285
|
+
* console.log(v.TotalWeaknesses); // 969
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
async version() {
|
|
289
|
+
return this.request("/cwe/version");
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Looks up multiple CWE entries by their numeric IDs in a single request.
|
|
293
|
+
*
|
|
294
|
+
* `GET /cwe/{ids}` (comma-separated)
|
|
295
|
+
*
|
|
296
|
+
* @param ids - Array of CWE numeric IDs (e.g. `[74, 79]`)
|
|
297
|
+
* @returns Array of lightweight CWE entries with type and ID
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* const entries = await cwe.lookup([74, 79]);
|
|
302
|
+
* entries.forEach(e => console.log(e.ID, e.Type));
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
async lookup(ids) {
|
|
306
|
+
return this.request(`/cwe/${ids.join(",")}`);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Returns a {@link WeaknessResource} for a given CWE weakness ID, providing
|
|
310
|
+
* access to weakness details and its hierarchy (parents, children, ancestors,
|
|
311
|
+
* descendants).
|
|
312
|
+
*
|
|
313
|
+
* The returned resource can be awaited directly to fetch the full weakness,
|
|
314
|
+
* or chained to access hierarchy methods.
|
|
315
|
+
*
|
|
316
|
+
* @param id - The CWE weakness numeric ID (e.g. `79`)
|
|
317
|
+
* @returns A chainable weakness resource
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* const weakness = await cwe.weakness(79);
|
|
322
|
+
* const parents = await cwe.weakness(74).parents(1000);
|
|
323
|
+
* const tree = await cwe.weakness(74).descendants(1000);
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
weakness(id) {
|
|
327
|
+
return new WeaknessResource(
|
|
328
|
+
(path, params) => this.request(path, params),
|
|
329
|
+
id
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Returns a {@link CategoryResource} for a given CWE category ID, providing
|
|
334
|
+
* access to category details.
|
|
335
|
+
*
|
|
336
|
+
* The returned resource can be awaited directly to fetch the full category.
|
|
337
|
+
*
|
|
338
|
+
* @param id - The CWE category numeric ID (e.g. `189`)
|
|
339
|
+
* @returns A chainable category resource
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* const category = await cwe.category(189);
|
|
344
|
+
* console.log(category.Name); // 'Numeric Errors'
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
category(id) {
|
|
348
|
+
return new CategoryResource(
|
|
349
|
+
(path, params) => this.request(path, params),
|
|
350
|
+
id
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Returns a {@link ViewResource} for a given CWE view ID, providing access
|
|
355
|
+
* to view details.
|
|
356
|
+
*
|
|
357
|
+
* The returned resource can be awaited directly to fetch the full view.
|
|
358
|
+
*
|
|
359
|
+
* @param id - The CWE view numeric ID (e.g. `1425`)
|
|
360
|
+
* @returns A chainable view resource
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* const view = await cwe.view(1425);
|
|
365
|
+
* console.log(view.Name); // 'Weaknesses in the 2023 CWE Top 25...'
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
view(id) {
|
|
369
|
+
return new ViewResource(
|
|
370
|
+
(path, params) => this.request(path, params),
|
|
371
|
+
id
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
function buildUrl(base, params) {
|
|
376
|
+
if (!params) return base;
|
|
377
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
378
|
+
if (entries.length === 0) return base;
|
|
379
|
+
const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));
|
|
380
|
+
return `${base}?${search.toString()}`;
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors/CweApiError.ts","../src/resources/WeaknessResource.ts","../src/resources/CategoryResource.ts","../src/resources/ViewResource.ts","../src/CweClient.ts"],"sourcesContent":["export { CweClient } from './CweClient';\nexport { CweApiError } from './errors/CweApiError';\nexport type { CweClientOptions, RequestEvent, CweClientEvents } from './CweClient';\nexport { WeaknessResource } from './resources/WeaknessResource';\nexport { CategoryResource } from './resources/CategoryResource';\nexport { ViewResource } from './resources/ViewResource';\nexport type { CweVersion } from './domain/Version';\nexport type { CweEntry, CweEntryType } from './domain/CweEntry';\nexport type {\n CweWeakness,\n CweRelatedWeakness,\n CweWeaknessOrdinality,\n CweApplicablePlatform,\n CweAlternateTerm,\n} from './domain/Weakness';\nexport type {\n CweCategory,\n CweTaxonomyMapping,\n CweCategoryRelationship,\n CweCategoryReference,\n} from './domain/Category';\nexport type {\n CweView,\n CweViewAudience,\n CweViewMember,\n} from './domain/View';\nexport type {\n CweRelationEntry,\n CweAncestorNode,\n CweDescendantNode,\n} from './domain/Relations';\n","/**\n * Thrown when the CWE API returns a non-2xx response.\n *\n * @example\n * ```typescript\n * import { CweApiError } from 'cwe-api-client';\n *\n * try {\n * await cwe.weakness(99999).get();\n * } catch (err) {\n * if (err instanceof CweApiError) {\n * console.log(err.status); // 404\n * console.log(err.statusText); // 'Not Found'\n * console.log(err.message); // 'CWE API error: 404 Not Found'\n * }\n * }\n * ```\n */\nexport class CweApiError extends Error {\n /** HTTP status code (e.g. `404`, `400`, `500`) */\n readonly status: number;\n /** HTTP status text (e.g. `'Not Found'`, `'Bad Request'`) */\n readonly statusText: string;\n\n constructor(status: number, statusText: string) {\n super(`CWE API error: ${status} ${statusText}`);\n this.name = 'CweApiError';\n this.status = status;\n this.statusText = statusText;\n }\n}\n","import type { CweWeakness } from '../domain/Weakness';\nimport type { CweRelationEntry, CweAncestorNode, CweDescendantNode } from '../domain/Relations';\n\n/** @internal */\nexport type RequestFn = <T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n) => Promise<T>;\n\n/**\n * Represents a CWE weakness resource, providing access to weakness details\n * and its position in the CWE hierarchy.\n *\n * Implements `PromiseLike<CweWeakness>` so it can be awaited directly to\n * fetch the full weakness, while also exposing hierarchy methods.\n *\n * @example\n * ```typescript\n * // Await directly to get full weakness data\n * const weakness = await cwe.weakness(79);\n *\n * // Or call .get() explicitly\n * const weakness = await cwe.weakness(79).get();\n *\n * // Navigate the hierarchy\n * const parents = await cwe.weakness(74).parents(1000);\n * const children = await cwe.weakness(74).children(1000);\n * const ancestors = await cwe.weakness(74).ancestors(1000);\n * const descendants = await cwe.weakness(74).descendants(1000);\n * ```\n */\nexport class WeaknessResource implements PromiseLike<CweWeakness> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly id: number,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the full weakness.\n * Delegates to {@link WeaknessResource.get}.\n */\n then<TResult1 = CweWeakness, TResult2 = never>(\n onfulfilled?: ((value: CweWeakness) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the full weakness entry.\n *\n * `GET /cwe/weakness/{id}`\n *\n * @returns The weakness object\n */\n async get(): Promise<CweWeakness> {\n const data = await this.request<{ Weaknesses: CweWeakness[] }>(`/cwe/weakness/${this.id}`);\n return data.Weaknesses[0];\n }\n\n /**\n * Fetches the direct parents of this weakness in a given view.\n *\n * `GET /cwe/{id}/parents?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Array of direct parent entries\n *\n * @example\n * ```typescript\n * const parents = await cwe.weakness(74).parents(1000);\n * ```\n */\n async parents(viewId?: number): Promise<CweRelationEntry[]> {\n return this.request<CweRelationEntry[]>(\n `/cwe/${this.id}/parents`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n\n /**\n * Fetches the direct children of this weakness in a given view.\n *\n * `GET /cwe/{id}/children?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Array of direct child entries\n *\n * @example\n * ```typescript\n * const children = await cwe.weakness(74).children(1000);\n * ```\n */\n async children(viewId?: number): Promise<CweRelationEntry[]> {\n return this.request<CweRelationEntry[]>(\n `/cwe/${this.id}/children`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n\n /**\n * Fetches the full ancestor tree of this weakness in a given view.\n *\n * `GET /cwe/{id}/ancestors?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Recursive ancestor tree from this node up to the view root\n *\n * @example\n * ```typescript\n * const tree = await cwe.weakness(74).ancestors(1000);\n * ```\n */\n async ancestors(viewId?: number): Promise<CweAncestorNode[]> {\n return this.request<CweAncestorNode[]>(\n `/cwe/${this.id}/ancestors`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n\n /**\n * Fetches the full descendant tree of this weakness in a given view.\n *\n * `GET /cwe/{id}/descendants?view={viewId}`\n *\n * @param viewId - CWE view ID to scope the hierarchy (e.g. `1000`)\n * @returns Recursive descendant tree from this node down to leaf entries\n *\n * @example\n * ```typescript\n * const tree = await cwe.weakness(74).descendants(1000);\n * ```\n */\n async descendants(viewId?: number): Promise<CweDescendantNode[]> {\n return this.request<CweDescendantNode[]>(\n `/cwe/${this.id}/descendants`,\n viewId !== undefined ? { view: viewId } : undefined,\n );\n }\n}\n","import type { CweCategory } from '../domain/Category';\nimport type { RequestFn } from './WeaknessResource';\n\n/**\n * Represents a CWE category resource, providing access to category details.\n *\n * Implements `PromiseLike<CweCategory>` so it can be awaited directly.\n *\n * @example\n * ```typescript\n * // Await directly to get full category data\n * const category = await cwe.category(189);\n *\n * // Or call .get() explicitly\n * const category = await cwe.category(189).get();\n * ```\n */\nexport class CategoryResource implements PromiseLike<CweCategory> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly id: number,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the full category.\n * Delegates to {@link CategoryResource.get}.\n */\n then<TResult1 = CweCategory, TResult2 = never>(\n onfulfilled?: ((value: CweCategory) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the full category entry.\n *\n * `GET /cwe/category/{id}`\n *\n * @returns The category object\n */\n async get(): Promise<CweCategory> {\n const data = await this.request<{ Categories: CweCategory[] }>(`/cwe/category/${this.id}`);\n return data.Categories[0];\n }\n}\n","import type { CweView } from '../domain/View';\nimport type { RequestFn } from './WeaknessResource';\n\n/**\n * Represents a CWE view resource, providing access to view details.\n *\n * Implements `PromiseLike<CweView>` so it can be awaited directly.\n *\n * @example\n * ```typescript\n * // Await directly to get full view data\n * const view = await cwe.view(1425);\n *\n * // Or call .get() explicitly\n * const view = await cwe.view(1425).get();\n * ```\n */\nexport class ViewResource implements PromiseLike<CweView> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly id: number,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the full view.\n * Delegates to {@link ViewResource.get}.\n */\n then<TResult1 = CweView, TResult2 = never>(\n onfulfilled?: ((value: CweView) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the full view entry.\n *\n * `GET /cwe/view/{id}`\n *\n * @returns The view object\n */\n async get(): Promise<CweView> {\n const data = await this.request<{ Views: CweView[] }>(`/cwe/view/${this.id}`);\n return data.Views[0];\n }\n}\n","import { CweApiError } from './errors/CweApiError';\nimport { WeaknessResource } from './resources/WeaknessResource';\nimport { CategoryResource } from './resources/CategoryResource';\nimport { ViewResource } from './resources/ViewResource';\nimport type { CweVersion } from './domain/Version';\nimport type { CweEntry } from './domain/CweEntry';\n\nconst DEFAULT_BASE_URL = 'https://cwe-api.mitre.org/api/v1';\n\n/**\n * Payload emitted on every HTTP request made by {@link CweClient}.\n */\nexport interface RequestEvent {\n /** Full URL that was requested */\n url: string;\n /** HTTP method used */\n method: 'GET';\n /** Timestamp when the request started */\n startedAt: Date;\n /** Timestamp when the request finished (success or error) */\n finishedAt: Date;\n /** Total duration in milliseconds */\n durationMs: number;\n /** HTTP status code returned by the server, if a response was received */\n statusCode?: number;\n /** Error thrown, if the request failed */\n error?: Error;\n}\n\n/** Map of supported client events to their callback signatures */\nexport interface CweClientEvents {\n request: (event: RequestEvent) => void;\n}\n\n/**\n * Constructor options for {@link CweClient}.\n */\nexport interface CweClientOptions {\n /**\n * Base URL for the CWE API (default: `'https://cwe-api.mitre.org/api/v1'`).\n * Override for mirrors or local instances.\n */\n baseUrl?: string;\n}\n\n/**\n * Main entry point for the MITRE CWE API client.\n *\n * @example\n * ```typescript\n * import { CweClient } from 'cwe-api-client';\n *\n * const cwe = new CweClient();\n *\n * // Get content version metadata\n * const version = await cwe.version();\n *\n * // Look up multiple CWEs at once\n * const entries = await cwe.lookup([74, 79]);\n *\n * // Get full weakness details\n * const weakness = await cwe.weakness(79);\n *\n * // Navigate the hierarchy\n * const parents = await cwe.weakness(74).parents(1000);\n * const descendants = await cwe.weakness(74).descendants(1000);\n *\n * // Get category and view details\n * const category = await cwe.category(189);\n * const view = await cwe.view(1425);\n * ```\n */\nexport class CweClient {\n private readonly baseUrl: string;\n private readonly listeners: Map<\n keyof CweClientEvents,\n CweClientEvents[keyof CweClientEvents][]\n > = new Map();\n\n /**\n * @param options - Optional configuration for the API base URL\n */\n constructor(options: CweClientOptions = {}) {\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n }\n\n /**\n * Subscribes to a client event.\n *\n * @example\n * ```typescript\n * cwe.on('request', (event) => {\n * console.log(`${event.method} ${event.url} — ${event.durationMs}ms`);\n * if (event.error) console.error('Request failed:', event.error);\n * });\n * ```\n */\n on<K extends keyof CweClientEvents>(event: K, callback: CweClientEvents[K]): this {\n const callbacks = this.listeners.get(event) ?? [];\n callbacks.push(callback);\n this.listeners.set(event, callbacks);\n return this;\n }\n\n private emit<K extends keyof CweClientEvents>(\n event: K,\n payload: Parameters<CweClientEvents[K]>[0],\n ): void {\n const callbacks = this.listeners.get(event) ?? [];\n for (const cb of callbacks) {\n (cb as (p: typeof payload) => void)(payload);\n }\n }\n\n /**\n * Performs a GET request to the CWE API.\n *\n * @param path - Path to append to the base URL (e.g. `/cwe/version`)\n * @param params - Optional query parameters\n * @internal\n */\n async request<T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n ): Promise<T> {\n const url = buildUrl(`${this.baseUrl}${path}`, params);\n const startedAt = new Date();\n let statusCode: number | undefined;\n try {\n const response = await fetch(url, {\n headers: { Accept: 'application/json' },\n });\n statusCode = response.status;\n if (!response.ok) {\n throw new CweApiError(response.status, response.statusText);\n }\n const data = await response.json() as T;\n this.emit('request', {\n url,\n method: 'GET',\n startedAt,\n finishedAt: new Date(),\n durationMs: Date.now() - startedAt.getTime(),\n statusCode,\n });\n return data;\n } catch (err) {\n const finishedAt = new Date();\n this.emit('request', {\n url,\n method: 'GET',\n startedAt,\n finishedAt,\n durationMs: finishedAt.getTime() - startedAt.getTime(),\n statusCode,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n throw err;\n }\n }\n\n /**\n * Fetches CWE content version metadata.\n *\n * `GET /cwe/version`\n *\n * @returns Version metadata including content version, date, and counts\n *\n * @example\n * ```typescript\n * const v = await cwe.version();\n * console.log(v.ContentVersion); // '4.19.1'\n * console.log(v.TotalWeaknesses); // 969\n * ```\n */\n async version(): Promise<CweVersion> {\n return this.request<CweVersion>('/cwe/version');\n }\n\n /**\n * Looks up multiple CWE entries by their numeric IDs in a single request.\n *\n * `GET /cwe/{ids}` (comma-separated)\n *\n * @param ids - Array of CWE numeric IDs (e.g. `[74, 79]`)\n * @returns Array of lightweight CWE entries with type and ID\n *\n * @example\n * ```typescript\n * const entries = await cwe.lookup([74, 79]);\n * entries.forEach(e => console.log(e.ID, e.Type));\n * ```\n */\n async lookup(ids: number[]): Promise<CweEntry[]> {\n return this.request<CweEntry[]>(`/cwe/${ids.join(',')}`);\n }\n\n /**\n * Returns a {@link WeaknessResource} for a given CWE weakness ID, providing\n * access to weakness details and its hierarchy (parents, children, ancestors,\n * descendants).\n *\n * The returned resource can be awaited directly to fetch the full weakness,\n * or chained to access hierarchy methods.\n *\n * @param id - The CWE weakness numeric ID (e.g. `79`)\n * @returns A chainable weakness resource\n *\n * @example\n * ```typescript\n * const weakness = await cwe.weakness(79);\n * const parents = await cwe.weakness(74).parents(1000);\n * const tree = await cwe.weakness(74).descendants(1000);\n * ```\n */\n weakness(id: number): WeaknessResource {\n return new WeaknessResource(\n <T>(path: string, params?: Record<string, string | number | boolean>) =>\n this.request<T>(path, params),\n id,\n );\n }\n\n /**\n * Returns a {@link CategoryResource} for a given CWE category ID, providing\n * access to category details.\n *\n * The returned resource can be awaited directly to fetch the full category.\n *\n * @param id - The CWE category numeric ID (e.g. `189`)\n * @returns A chainable category resource\n *\n * @example\n * ```typescript\n * const category = await cwe.category(189);\n * console.log(category.Name); // 'Numeric Errors'\n * ```\n */\n category(id: number): CategoryResource {\n return new CategoryResource(\n <T>(path: string, params?: Record<string, string | number | boolean>) =>\n this.request<T>(path, params),\n id,\n );\n }\n\n /**\n * Returns a {@link ViewResource} for a given CWE view ID, providing access\n * to view details.\n *\n * The returned resource can be awaited directly to fetch the full view.\n *\n * @param id - The CWE view numeric ID (e.g. `1425`)\n * @returns A chainable view resource\n *\n * @example\n * ```typescript\n * const view = await cwe.view(1425);\n * console.log(view.Name); // 'Weaknesses in the 2023 CWE Top 25...'\n * ```\n */\n view(id: number): ViewResource {\n return new ViewResource(\n <T>(path: string, params?: Record<string, string | number | boolean>) =>\n this.request<T>(path, params),\n id,\n );\n }\n}\n\n/**\n * Appends query parameters to a URL string, skipping `undefined` values.\n * @internal\n */\nfunction buildUrl(base: string, params?: Record<string, string | number | boolean>): string {\n if (!params) return base;\n const entries = Object.entries(params).filter(([, v]) => v !== undefined);\n if (entries.length === 0) return base;\n const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));\n return `${base}?${search.toString()}`;\n}\n"],"mappings":"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","names":[]}
|