@secureport/sdk 0.5.0

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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/client.d.ts +135 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +191 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/errors.d.ts +80 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +118 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/index.d.ts +31 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +10 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/pagination.d.ts +27 -0
  16. package/dist/pagination.d.ts.map +1 -0
  17. package/dist/pagination.js +19 -0
  18. package/dist/pagination.js.map +1 -0
  19. package/dist/resources/branding.d.ts +49 -0
  20. package/dist/resources/branding.d.ts.map +1 -0
  21. package/dist/resources/branding.js +38 -0
  22. package/dist/resources/branding.js.map +1 -0
  23. package/dist/resources/issues.d.ts +248 -0
  24. package/dist/resources/issues.d.ts.map +1 -0
  25. package/dist/resources/issues.js +100 -0
  26. package/dist/resources/issues.js.map +1 -0
  27. package/dist/resources/reports.d.ts +83 -0
  28. package/dist/resources/reports.d.ts.map +1 -0
  29. package/dist/resources/reports.js +50 -0
  30. package/dist/resources/reports.js.map +1 -0
  31. package/dist/resources/runs.d.ts +474 -0
  32. package/dist/resources/runs.d.ts.map +1 -0
  33. package/dist/resources/runs.js +281 -0
  34. package/dist/resources/runs.js.map +1 -0
  35. package/dist/resources/suppression-rules.d.ts +43 -0
  36. package/dist/resources/suppression-rules.d.ts.map +1 -0
  37. package/dist/resources/suppression-rules.js +33 -0
  38. package/dist/resources/suppression-rules.js.map +1 -0
  39. package/dist/resources/targets.d.ts +126 -0
  40. package/dist/resources/targets.d.ts.map +1 -0
  41. package/dist/resources/targets.js +83 -0
  42. package/dist/resources/targets.js.map +1 -0
  43. package/package.json +55 -0
  44. package/src/client.ts +328 -0
  45. package/src/errors.ts +120 -0
  46. package/src/index.ts +100 -0
  47. package/src/pagination.ts +41 -0
  48. package/src/resources/branding.ts +59 -0
  49. package/src/resources/issues.ts +344 -0
  50. package/src/resources/reports.ts +104 -0
  51. package/src/resources/runs.ts +635 -0
  52. package/src/resources/suppression-rules.ts +69 -0
  53. package/src/resources/targets.ts +179 -0
@@ -0,0 +1,281 @@
1
+ import { parseSnapshot } from '@secureport/core';
2
+ import { paginate } from '../pagination.js';
3
+ const DEFAULT_POLL_INTERVAL_MS = 5_000;
4
+ const DEFAULT_WAIT_TIMEOUT_MS = 900_000;
5
+ /**
6
+ * Runs — one execution against a target, and the scan lifecycle around it
7
+ * (`00-DOMAIN.md` §3): started, evidence attached, finished, then
8
+ * reconciled into a summary and a snapshot.
9
+ */
10
+ export class RunsResource {
11
+ request;
12
+ constructor(request) {
13
+ this.request = request;
14
+ }
15
+ list(query) {
16
+ return this.request({ method: 'GET', path: '/runs', query: { ...query } });
17
+ }
18
+ /** Every run matching `filters`, across every page, as an async iterator. */
19
+ listAll(filters) {
20
+ return paginate((page) => this.list({ ...filters, page }));
21
+ }
22
+ /**
23
+ * Starts a run against a target. It is created `running` and stays that way
24
+ * until {@link RunsResource.finish}: upload scanner output in between with
25
+ * {@link RunsResource.import}.
26
+ *
27
+ * @throws {SecureportError} `terms_required` for every kind if the
28
+ * organisation has not accepted the current terms — a human accepts them at
29
+ * `POST /orgs/{orgId}/terms`; an API key cannot. `verification_required` for
30
+ * `kind: 'scan'` if the target has no currently-passing verification,
31
+ * re-checked fresh as part of this same request rather than trusted from a
32
+ * stored flag. Neither is retried by the transport: both are settled
33
+ * refusals, not transient ones.
34
+ */
35
+ create(input) {
36
+ return this.request({ method: 'POST', path: '/runs', body: input });
37
+ }
38
+ /**
39
+ * One run by id. What it found is {@link RunsResource.summary}, once it has finished —
40
+ * this method never carries a summary or findings itself.
41
+ */
42
+ get(id) {
43
+ return this.request({ method: 'GET', path: `/runs/${encodeURIComponent(id)}` });
44
+ }
45
+ /**
46
+ * Ends a run and, for `status: 'finished'`, reconciles it: the findings
47
+ * uploaded against it become new, still-open, resolved or regressed
48
+ * issues, and the run gets a summary. **`status: 'failed'` reconciles
49
+ * nothing** — see {@link FinishRunInput.status}.
50
+ *
51
+ * @remarks Uploads and manual runs only: a `kind: 'scan'` run is refused here — its engine reports its own end, with evidence this route cannot supply. Stop a scan early with {@link RunsResource.cancel} instead.
52
+ * @throws {SecureportError} `conflict` if any upload is still being
53
+ * parsed — poll {@link RunsResource.listArtifacts} and finish once every
54
+ * one is `done` or `failed` — or if the run had already ended;
55
+ * `validation` for a `kind: 'scan'` run.
56
+ */
57
+ finish(id, input) {
58
+ return this.request({
59
+ method: 'POST',
60
+ path: `/runs/${encodeURIComponent(id)}/finish`,
61
+ body: input,
62
+ });
63
+ }
64
+ /**
65
+ * Stops a running scan: the run ends as `'cancelled'` immediately, and the
66
+ * scan job behind it is cancelled best-effort.
67
+ *
68
+ * @remarks If the job cancel fails, the engine simply runs out its time budget and its late reports are discarded, so `'cancelled'` is final either way. Nothing is reconciled — a cancelled run resolves no issues, and findings already ingested stay recorded as partial evidence. That intent is what distinguishes it from `'failed'`, which means the scan itself went wrong.
69
+ * @throws {SecureportError} `conflict` if the run had already ended;
70
+ * `validation` for a run that is not a `kind: 'scan'` — an upload is ended
71
+ * by its own caller via {@link RunsResource.finish}.
72
+ */
73
+ cancel(id) {
74
+ return this.request({ method: 'POST', path: `/runs/${encodeURIComponent(id)}/cancel` });
75
+ }
76
+ /**
77
+ * Where a scan is: its phase, how long it has run, its time budget, and
78
+ * when it will have ended by — see {@link ScanProgress} for why no percentage.
79
+ *
80
+ * @remarks Combine with {@link RunsResource.waitUntilFinished} for a poll that also knows when to give up: `expectedBy` is the honest bound.
81
+ * @throws {SecureportError} `validation` for a run that is not a
82
+ * `kind: 'scan'` — an upload's progress is whatever its caller is doing.
83
+ */
84
+ progress(id) {
85
+ return this.request({ method: 'GET', path: `/runs/${encodeURIComponent(id)}/progress` });
86
+ }
87
+ /**
88
+ * Archives a raw scanner artifact against a run. Nothing is parsed on the
89
+ * client side — the whole file is handed to the API as `input.body`, with
90
+ * `input.contentType` sent verbatim, and the API decides how (and when) to
91
+ * parse it.
92
+ *
93
+ * **The response shape depends on size, not just success or failure.** A
94
+ * file under 5 MiB is parsed inline and answers `201` with
95
+ * `processingStatus: 'done'` and `findingCount`/`skippedLines` filled in. At
96
+ * or over 5 MiB the parse is queued and the API answers `202` with
97
+ * `processingStatus: 'queued'` instead — poll {@link RunsResource.listArtifacts}
98
+ * for the outcome. Both are success: check `processingStatus` on the result rather
99
+ * than assuming the synchronous shape. **25 MiB is a hard cap**, refused
100
+ * with `payload_too_large` rather than accepted and truncated.
101
+ *
102
+ * **Idempotent by content, not by request.** Re-uploading the exact same
103
+ * bytes against the same run answers `200` with the original row, verbatim
104
+ * — no re-parse — so a retried upload after a flaky network is never
105
+ * double-counted.
106
+ *
107
+ * @throws {SecureportError} `conflict` if the run has already ended;
108
+ * `payload_too_large` over 25 MiB; `unsupported_media_type` for a
109
+ * `contentType` the API does not accept, or a compressed body.
110
+ */
111
+ import(id, input) {
112
+ return this.request({
113
+ method: 'POST',
114
+ path: `/runs/${encodeURIComponent(id)}/import`,
115
+ query: { engine: input.engine, filename: input.filename },
116
+ rawBody: { data: input.body, contentType: input.contentType },
117
+ });
118
+ }
119
+ /**
120
+ * The first half of the signed-URL upload path for a file too large for
121
+ * {@link RunsResource.import} — Cloud Run caps that route's request body at
122
+ * 32 MiB whatever the API wants, so a genuinely large scan (a big Nessus or
123
+ * Burp export) goes here instead: mint a URL, `PUT` the file straight to
124
+ * the bucket yourself, then {@link RunsResource.completeUpload}.
125
+ *
126
+ * **Returns one of two shapes, told apart structurally.** If the same
127
+ * `sha256` is already archived on this run, this resolves with the
128
+ * existing {@link RunArtifact} directly — nothing minted, nothing to
129
+ * upload. Otherwise it resolves with a {@link RunUploadUrl}. Check for
130
+ * `'uploadUrl' in result` (or any field {@link RunUploadUrl} has and
131
+ * {@link RunArtifact} does not) to tell them apart.
132
+ *
133
+ * **You must `PUT` the bytes yourself, with a plain `fetch` — not through
134
+ * this SDK's own transport.** `uploadUrl` is a signed Cloud Storage URL:
135
+ * this client's `request`/`requestRaw` always attach your API key as a
136
+ * bearer token and always resolve paths against the API's own base URL,
137
+ * neither of which belongs on a request to Cloud Storage. Send every
138
+ * header in `result.requiredHeaders` verbatim — the bucket's signature
139
+ * check refuses the upload otherwise, silently from this SDK's point of
140
+ * view (it never sees that request).
141
+ *
142
+ * @throws {SecureportError} `conflict` if the run has already ended;
143
+ * `unsupported_media_type` for a `contentType` the API does not accept.
144
+ */
145
+ createUploadUrl(id, input) {
146
+ return this.request({
147
+ method: 'POST',
148
+ path: `/runs/${encodeURIComponent(id)}/import-url`,
149
+ body: input,
150
+ });
151
+ }
152
+ /**
153
+ * The second half of the signed-URL upload path — call once your own
154
+ * `PUT` to {@link RunUploadUrl.uploadUrl} has succeeded. Archives the
155
+ * object as a run artifact and queues it for parsing; unlike
156
+ * {@link RunsResource.import}, the result is never parsed synchronously
157
+ * here — anything that needed a signed URL is already well past the size
158
+ * the API parses inline. Poll {@link RunsResource.listArtifacts} (or use
159
+ * {@link RunsResource.waitForArtifact}) the same way a `202` from
160
+ * {@link RunsResource.import} is polled.
161
+ *
162
+ * **Idempotent by content, same as {@link RunsResource.import}.** Calling
163
+ * this twice for the same object answers `200` with the original row the
164
+ * second time.
165
+ *
166
+ * @throws {SecureportError} `conflict` if the run has already ended, or if
167
+ * nothing was actually uploaded to the signed URL (it may have expired).
168
+ */
169
+ completeUpload(id, input) {
170
+ return this.request({
171
+ method: 'POST',
172
+ path: `/runs/${encodeURIComponent(id)}/import-url/complete`,
173
+ body: input,
174
+ });
175
+ }
176
+ /**
177
+ * The uploads archived against a run, with where each parse stands — what
178
+ * a client polls after a `202` from {@link RunsResource.import}. Not
179
+ * paginated: a run holds the handful of files one scan produced.
180
+ */
181
+ listArtifacts(id) {
182
+ return this.request({ method: 'GET', path: `/runs/${encodeURIComponent(id)}/artifacts` });
183
+ }
184
+ /**
185
+ * What the run did, in the terms a report opens with.
186
+ *
187
+ * @throws {SecureportError} `conflict` if the run has not been reconciled
188
+ * — still running, or ended `failed` — rather than a zeroed summary that
189
+ * would read as "nothing found".
190
+ */
191
+ summary(id) {
192
+ return this.request({ method: 'GET', path: `/runs/${encodeURIComponent(id)}/summary` });
193
+ }
194
+ /**
195
+ * The snapshot a report is rendered from: the run summary, the target,
196
+ * every issue with what this run changed about it, and the suppressed
197
+ * appendix.
198
+ *
199
+ * **Returns `@secureport/core`'s own `Snapshot` type, with real `Date`s —
200
+ * the one method in this resource that does.** Every other method here
201
+ * returns ISO 8601 strings, mirroring the wire exactly; this one instead
202
+ * runs the response text through core's own `parseSnapshot`, since that
203
+ * is the exact function a `Snapshot` written to disk is read back
204
+ * with, and the API's wire shape is asserted `Exact<>` against core's type
205
+ * server-side. That is what makes `@secureport/core` this package's first
206
+ * real dependency, rather than a structural mirror.
207
+ *
208
+ * @throws {SecureportError} `conflict` if the run has not been reconciled
209
+ * — still running, or ended `failed`.
210
+ */
211
+ async snapshot(id) {
212
+ const body = await this.request({
213
+ method: 'GET',
214
+ path: `/runs/${encodeURIComponent(id)}/snapshot`,
215
+ });
216
+ return parseSnapshot(JSON.stringify(body));
217
+ }
218
+ /**
219
+ * Polls {@link RunsResource.get} until the run leaves `'running'`,
220
+ * resolving with whatever it finished as. **Does not itself call
221
+ * {@link RunsResource.summary} or {@link RunsResource.snapshot}** — those
222
+ * still 409 until reconciliation completes, which is why this only
223
+ * watches `status`.
224
+ *
225
+ * Shaped for a long-running hosted scan from the start: today an upload
226
+ * run is finished by the caller directly, so this helper's payoff is
227
+ * mostly forward-looking until scans actually execute.
228
+ *
229
+ * @throws {Error} if `status` is still `'running'` once `timeoutMs` elapses.
230
+ */
231
+ async waitUntilFinished(id, options) {
232
+ const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
233
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
234
+ const deadline = Date.now() + timeoutMs;
235
+ for (;;) {
236
+ const run = await this.get(id);
237
+ if (run.status !== 'running')
238
+ return run;
239
+ if (Date.now() + pollIntervalMs > deadline) {
240
+ throw new Error(`run ${id} did not finish within ${String(timeoutMs)}ms (still running).`);
241
+ }
242
+ await sleep(pollIntervalMs);
243
+ }
244
+ }
245
+ /**
246
+ * Polls {@link RunsResource.listArtifacts} until one artifact's
247
+ * `processingStatus` leaves `'queued'`/`'processing'`, resolving with it —
248
+ * the artifact-level completion {@link RunsResource.finish} itself waits
249
+ * on (its own TSDoc: "poll listArtifacts and finish once every one is done
250
+ * or failed"). A large upload answers {@link RunsResource.import} with
251
+ * `202 queued` before the parse has even started; this is what a caller
252
+ * that started one polls instead of hand-rolling the same loop.
253
+ *
254
+ * @throws {Error} if the artifact never appears on the run (id mismatch),
255
+ * or if it is still `queued`/`processing` once `timeoutMs` elapses.
256
+ */
257
+ async waitForArtifact(runId, artifactId, options) {
258
+ const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
259
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
260
+ const deadline = Date.now() + timeoutMs;
261
+ for (;;) {
262
+ const { data } = await this.listArtifacts(runId);
263
+ const artifact = data.find((a) => a.id === artifactId);
264
+ if (artifact === undefined) {
265
+ throw new Error(`artifact ${artifactId} not found on run ${runId}.`);
266
+ }
267
+ if (artifact.processingStatus !== 'queued' && artifact.processingStatus !== 'processing') {
268
+ return artifact;
269
+ }
270
+ if (Date.now() + pollIntervalMs > deadline) {
271
+ throw new Error(`artifact ${artifactId} did not finish processing within ${String(timeoutMs)}ms ` +
272
+ `(still ${artifact.processingStatus}).`);
273
+ }
274
+ await sleep(pollIntervalMs);
275
+ }
276
+ }
277
+ }
278
+ function sleep(ms) {
279
+ return new Promise((resolve) => setTimeout(resolve, ms));
280
+ }
281
+ //# sourceMappingURL=runs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.js","sourceRoot":"","sources":["../../src/resources/runs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { Requester } from '../client.js';
2
+ import type { Page, PageQuery } from '../pagination.js';
3
+ /**
4
+ * A pre-emptive suppression rule. `createdAt` is an ISO 8601 string, as the
5
+ * API sends it — parse it yourself if you need a `Date`.
6
+ */
7
+ export interface SuppressionRule {
8
+ readonly id: string;
9
+ readonly orgId: string;
10
+ /** `null` means the rule covers every target in the organisation. */
11
+ readonly targetId: string | null;
12
+ /** Matched against an issue's vulnerability key; `*` is the only wildcard. */
13
+ readonly vulnKeyGlob: string;
14
+ readonly reason: string;
15
+ /** The authenticated actor that created it — never supplied by the caller. */
16
+ readonly createdBy: string;
17
+ readonly createdAt: string;
18
+ }
19
+ export type SuppressionRulePage = Page<SuppressionRule>;
20
+ export interface NewSuppressionRule {
21
+ /** Omit to cover the whole organisation. */
22
+ readonly targetId?: string;
23
+ /** Anchored at both ends, so `xss-*` does not match `not-xss-reflected`. */
24
+ readonly vulnKeyGlob: string;
25
+ readonly reason: string;
26
+ }
27
+ /**
28
+ * Pre-emptive "never open an issue for this class" rules — the blunt
29
+ * instrument; per-issue ignore is the default. No update: delete and recreate.
30
+ */
31
+ export declare class SuppressionRulesResource {
32
+ private readonly request;
33
+ constructor(request: Requester);
34
+ list(query?: PageQuery): Promise<SuppressionRulePage>;
35
+ /** Every rule, across every page, as an async iterator. */
36
+ listAll(): AsyncIterable<SuppressionRule>;
37
+ /** @throws {SecureportError} `not_found` if `targetId` names no target of yours. */
38
+ create(input: NewSuppressionRule): Promise<SuppressionRule>;
39
+ get(id: string): Promise<SuppressionRule>;
40
+ /** Issues the rule already caused to open as ignored stay ignored. */
41
+ delete(id: string): Promise<void>;
42
+ }
43
+ //# sourceMappingURL=suppression-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppression-rules.d.ts","sourceRoot":"","sources":["../../src/resources/suppression-rules.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGxD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;AAExD,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,SAAS;IAE/C,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIrD,2DAA2D;IAC3D,OAAO,IAAI,aAAa,CAAC,eAAe,CAAC;IAIzC,oFAAoF;IACpF,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC;IAI3D,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAIzC,sEAAsE;IACtE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMlC"}
@@ -0,0 +1,33 @@
1
+ import { paginate } from '../pagination.js';
2
+ /**
3
+ * Pre-emptive "never open an issue for this class" rules — the blunt
4
+ * instrument; per-issue ignore is the default. No update: delete and recreate.
5
+ */
6
+ export class SuppressionRulesResource {
7
+ request;
8
+ constructor(request) {
9
+ this.request = request;
10
+ }
11
+ list(query) {
12
+ return this.request({ method: 'GET', path: '/suppression-rules', query: { ...query } });
13
+ }
14
+ /** Every rule, across every page, as an async iterator. */
15
+ listAll() {
16
+ return paginate((page) => this.list({ page }));
17
+ }
18
+ /** @throws {SecureportError} `not_found` if `targetId` names no target of yours. */
19
+ create(input) {
20
+ return this.request({ method: 'POST', path: '/suppression-rules', body: input });
21
+ }
22
+ get(id) {
23
+ return this.request({ method: 'GET', path: `/suppression-rules/${encodeURIComponent(id)}` });
24
+ }
25
+ /** Issues the rule already caused to open as ignored stay ignored. */
26
+ delete(id) {
27
+ return this.request({
28
+ method: 'DELETE',
29
+ path: `/suppression-rules/${encodeURIComponent(id)}`,
30
+ });
31
+ }
32
+ }
33
+ //# sourceMappingURL=suppression-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppression-rules.js","sourceRoot":"","sources":["../../src/resources/suppression-rules.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAiC5C;;;GAGG;AACH,MAAM,OAAO,wBAAwB;IACN;IAA7B,YAA6B,OAAkB;QAAlB,YAAO,GAAP,OAAO,CAAW;IAAG,CAAC;IAEnD,IAAI,CAAC,KAAiB;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,2DAA2D;IAC3D,OAAO;QACL,OAAO,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,oFAAoF;IACpF,MAAM,CAAC,KAAyB;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sBAAsB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,sEAAsE;IACtE,MAAM,CAAC,EAAU;QACf,OAAO,IAAI,CAAC,OAAO,CAAC;YAClB,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,sBAAsB,kBAAkB,CAAC,EAAE,CAAC,EAAE;SACrD,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,126 @@
1
+ import type { Requester } from '../client.js';
2
+ import type { Page, PageQuery } from '../pagination.js';
3
+ /** A target: one thing runs are executed against. */
4
+ export interface Target {
5
+ readonly id: string;
6
+ readonly orgId: string;
7
+ /** As it appears on a report cover. */
8
+ readonly name: string;
9
+ /** A URL, host or repository. */
10
+ readonly url: string;
11
+ }
12
+ export type TargetPage = Page<Target>;
13
+ export interface NewTarget {
14
+ readonly name: string;
15
+ readonly url: string;
16
+ }
17
+ /**
18
+ * At least one of `name`/`url` is required — the API answers `{}` with a
19
+ * `422 validation`, not a no-op.
20
+ */
21
+ export interface TargetPatch {
22
+ readonly name?: string;
23
+ readonly url?: string;
24
+ }
25
+ export type VerificationMethod = 'dns_txt' | 'http_file';
26
+ export type VerificationState = 'unverified' | 'verified';
27
+ /**
28
+ * The target's current verification state. Never carries the token — see
29
+ * {@link IssuedTargetVerification}, the one response that does.
30
+ *
31
+ * Date fields are ISO 8601 strings, as the API sends them — this type is
32
+ * SDK-local (no `@secureport/core` equivalent exists), so there is no
33
+ * shared revival logic to lean on; parse them yourself if you need `Date`.
34
+ */
35
+ export interface TargetVerification {
36
+ readonly method: VerificationMethod | null;
37
+ readonly state: VerificationState;
38
+ readonly issuedAt: string | null;
39
+ /** Set only while a rotated token's grace period is still active. */
40
+ readonly previousExpiresAt: string | null;
41
+ }
42
+ export interface NewTargetVerification {
43
+ readonly method: VerificationMethod;
44
+ }
45
+ /**
46
+ * The response to issuing (or rotating) a verification token.
47
+ *
48
+ * **The token appears here and nowhere else** — {@link TargetVerification}
49
+ * never carries it, and there is no recovery endpoint. Losing it means
50
+ * issuing a new one, which may reset `state` to `unverified` (see
51
+ * {@link TargetsResource.issueVerification}).
52
+ *
53
+ * `recordName`/`recordValue` are present for `method: 'dns_txt'`;
54
+ * `filePath`/`fileContents` for `method: 'http_file'` — a discriminated
55
+ * pair the wire schema does not enforce, so narrow on `method` yourself.
56
+ */
57
+ export interface IssuedTargetVerification extends Omit<TargetVerification, 'method'> {
58
+ readonly method: VerificationMethod;
59
+ readonly token: string;
60
+ readonly summary: string;
61
+ readonly recordName?: string;
62
+ readonly recordValue?: string;
63
+ readonly filePath?: string;
64
+ readonly fileContents?: string;
65
+ }
66
+ /** The result of one verification check — evidence, not a status flag. */
67
+ export interface TargetVerificationCheckResult {
68
+ /** This check's own evidence row id — a fresh one every call, whether it passed or failed. */
69
+ readonly id: string;
70
+ readonly verified: boolean;
71
+ readonly state: VerificationState;
72
+ readonly method: VerificationMethod;
73
+ readonly checkedAt: string;
74
+ readonly expected: string;
75
+ readonly observed: string | null;
76
+ readonly message: string;
77
+ }
78
+ /**
79
+ * Targets, and the per-target verification flow a hosted scan (`kind:
80
+ * 'scan'`) requires before it can start — see `POST /runs`'s
81
+ * `verification_required` gate.
82
+ */
83
+ export declare class TargetsResource {
84
+ private readonly request;
85
+ constructor(request: Requester);
86
+ list(query?: PageQuery): Promise<TargetPage>;
87
+ /** Every target, across every page, as an async iterator. */
88
+ listAll(): AsyncIterable<Target>;
89
+ create(input: NewTarget): Promise<Target>;
90
+ get(id: string): Promise<Target>;
91
+ /**
92
+ * **Changing `url` silently un-verifies the target** — the stored
93
+ * verification named the old host, and the API drops `state` to
94
+ * `unverified` as part of this same call. A caller relying on the target
95
+ * staying verified across a URL change needs to re-verify afterwards.
96
+ */
97
+ update(id: string, patch: TargetPatch): Promise<Target>;
98
+ /** @throws {SecureportError} `conflict` if the target has run or issue history. */
99
+ delete(id: string): Promise<void>;
100
+ getVerification(id: string): Promise<TargetVerification>;
101
+ /**
102
+ * Issues a fresh verification token, or rotates the current one — decided
103
+ * server-side, not by this method: calling with the **same `method`** as
104
+ * an already-verified target **rotates** (the target stays `verified`,
105
+ * and the old token keeps working for a 24-hour grace period). Calling
106
+ * with any other method, or while unverified, **re-issues** and resets
107
+ * `state` to `unverified` until the new token is checked.
108
+ */
109
+ issueVerification(id: string, input: NewTargetVerification): Promise<IssuedTargetVerification>;
110
+ /**
111
+ * Runs the verification check now, fresh — never trusts a stored flag.
112
+ * **Promote-only**: a match sets `state: 'verified'`; a mismatch or
113
+ * unreachable record is recorded as evidence but never demotes a target
114
+ * that is already verified. Writes a new evidence row every call, whether
115
+ * it passes or fails — not a no-op check.
116
+ *
117
+ * @throws {SecureportError} `conflict` if no token has been issued yet.
118
+ */
119
+ verify(id: string): Promise<TargetVerificationCheckResult>;
120
+ /**
121
+ * Revokes the current token outright — idempotent; revoking an
122
+ * already-unverified target is a clean no-op, not an error.
123
+ */
124
+ revokeVerification(id: string): Promise<void>;
125
+ }
126
+ //# sourceMappingURL=targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../../src/resources/targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { paginate } from '../pagination.js';
2
+ /**
3
+ * Targets, and the per-target verification flow a hosted scan (`kind:
4
+ * 'scan'`) requires before it can start — see `POST /runs`'s
5
+ * `verification_required` gate.
6
+ */
7
+ export class TargetsResource {
8
+ request;
9
+ constructor(request) {
10
+ this.request = request;
11
+ }
12
+ list(query) {
13
+ return this.request({ method: 'GET', path: '/targets', query: { ...query } });
14
+ }
15
+ /** Every target, across every page, as an async iterator. */
16
+ listAll() {
17
+ return paginate((page) => this.list({ page }));
18
+ }
19
+ create(input) {
20
+ return this.request({ method: 'POST', path: '/targets', body: input });
21
+ }
22
+ get(id) {
23
+ return this.request({ method: 'GET', path: `/targets/${encodeURIComponent(id)}` });
24
+ }
25
+ /**
26
+ * **Changing `url` silently un-verifies the target** — the stored
27
+ * verification named the old host, and the API drops `state` to
28
+ * `unverified` as part of this same call. A caller relying on the target
29
+ * staying verified across a URL change needs to re-verify afterwards.
30
+ */
31
+ update(id, patch) {
32
+ return this.request({
33
+ method: 'PATCH',
34
+ path: `/targets/${encodeURIComponent(id)}`,
35
+ body: patch,
36
+ });
37
+ }
38
+ /** @throws {SecureportError} `conflict` if the target has run or issue history. */
39
+ delete(id) {
40
+ return this.request({ method: 'DELETE', path: `/targets/${encodeURIComponent(id)}` });
41
+ }
42
+ getVerification(id) {
43
+ return this.request({ method: 'GET', path: `/targets/${encodeURIComponent(id)}/verification` });
44
+ }
45
+ /**
46
+ * Issues a fresh verification token, or rotates the current one — decided
47
+ * server-side, not by this method: calling with the **same `method`** as
48
+ * an already-verified target **rotates** (the target stays `verified`,
49
+ * and the old token keeps working for a 24-hour grace period). Calling
50
+ * with any other method, or while unverified, **re-issues** and resets
51
+ * `state` to `unverified` until the new token is checked.
52
+ */
53
+ issueVerification(id, input) {
54
+ return this.request({
55
+ method: 'POST',
56
+ path: `/targets/${encodeURIComponent(id)}/verification`,
57
+ body: input,
58
+ });
59
+ }
60
+ /**
61
+ * Runs the verification check now, fresh — never trusts a stored flag.
62
+ * **Promote-only**: a match sets `state: 'verified'`; a mismatch or
63
+ * unreachable record is recorded as evidence but never demotes a target
64
+ * that is already verified. Writes a new evidence row every call, whether
65
+ * it passes or fails — not a no-op check.
66
+ *
67
+ * @throws {SecureportError} `conflict` if no token has been issued yet.
68
+ */
69
+ verify(id) {
70
+ return this.request({ method: 'POST', path: `/targets/${encodeURIComponent(id)}/verify` });
71
+ }
72
+ /**
73
+ * Revokes the current token outright — idempotent; revoking an
74
+ * already-unverified target is a clean no-op, not an error.
75
+ */
76
+ revokeVerification(id) {
77
+ return this.request({
78
+ method: 'DELETE',
79
+ path: `/targets/${encodeURIComponent(id)}/verification`,
80
+ });
81
+ }
82
+ }
83
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../../src/resources/targets.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAyF5C;;;;GAIG;AACH,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,OAAkB;QAAlB,YAAO,GAAP,OAAO,CAAW;IAAG,CAAC;IAEnD,IAAI,CAAC,KAAiB;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,6DAA6D;IAC7D,OAAO;QACL,OAAO,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,CAAC,KAAgB;QACrB,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,EAAU,EAAE,KAAkB;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC;YAClB,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,EAAE;YAC1C,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAED,mFAAmF;IACnF,MAAM,CAAC,EAAU;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,eAAe,CAAC,EAAU;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;IAClG,CAAC;IAED;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAU,EAAE,KAA4B;QACxD,OAAO,IAAI,CAAC,OAAO,CAAC;YAClB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,eAAe;YACvD,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAU;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED;;;OAGG;IACH,kBAAkB,CAAC,EAAU;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC;YAClB,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,YAAY,kBAAkB,CAAC,EAAE,CAAC,eAAe;SACxD,CAAC,CAAC;IACL,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@secureport/sdk",
3
+ "version": "0.5.0",
4
+ "description": "Typed client for the Secureport hosted API.",
5
+ "keywords": [
6
+ "security",
7
+ "pentest",
8
+ "penetration-testing",
9
+ "vulnerability",
10
+ "vulnerability-management",
11
+ "appsec",
12
+ "devsecops",
13
+ "security-reporting",
14
+ "sdk",
15
+ "api-client"
16
+ ],
17
+ "homepage": "https://secureport.io/",
18
+ "bugs": {
19
+ "email": "hello@secureport.io"
20
+ },
21
+ "type": "module",
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "LICENSE"
34
+ ],
35
+ "dependencies": {
36
+ "@secureport/core": "^2.4.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.20.3",
40
+ "typescript": "^5.7.2"
41
+ },
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/agbjordan/secureport.git",
46
+ "directory": "packages/sdk"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
53
+ "typecheck": "tsc --noEmit"
54
+ }
55
+ }