Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 8 additions & 20 deletions docs/scanner-imports/schema-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,14 @@ With the SDK, repeat the preparation call that created the submission, using
the same options. For `prepareExternalPublication()`, supply the original
export file. For `prepareNormalizedExternalPublication()`, supply the original
`ExternalFindings`. Check `preview.resumed` before calling `publish()`.
Use the same SDK version throughout a submission when possible.

For hosted Semgrep or Snyk imports, finish any pending request before sending
updated evidence for the same finding IDs. Selections with distinct
scanner-issued IDs can proceed independently. If the checkpoint records
only the source, finish that request before starting another for the same
source and destination.

**Resuming across SDK versions.** Keep the SDK input that produced the
checkpoint, including report context and caller-added fields. Serializing it
as JSON can change the values used to identify the approved request. Retry on
the original deployment. If the SDK can verify the saved selection, it reuses
the request IDs and skips batches with receipts. Otherwise it preserves the
checkpoint and stops before uploading. If it cannot identify the approved
findings, a new submission for the same source and destination can also be blocked.

After a repository rename, a checkpoint that records only the repository ID
can still be retried with the original normalized findings and report metadata.
Omit supplemental repository declarations for that retry. A new declaration
requires a matching catalog entry or a URL recorded in the approval.

**Upgrading from an earlier import client.** Finish pending Cloud submissions with
the CLI or SDK build that created their checkpoints. Keep the original input and
state directory. If the outcome is uncertain, check the private inbox job or
read back the repository’s source reports to identify the accepted findings
before preparing a fresh submission. The current client starts a new approval
for a submission made with the retired checkpoint format.
[Verify an import](README.md#verify-the-import)

See [results and exit codes](../../sdk/typescript/docs/cli.md#results-and-exit-codes)
for cumulative counts and interrupted readback.
26 changes: 8 additions & 18 deletions docs/scanner-imports/semgrep.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ when the project has no repository URL. The report records that mapping in
For SSH projects, the report stores the original address in `nativeProjectUrl` and the resolved HTTP(S) address in `project.url`.
It records any supplied mapping separately. Save the complete output from a successful
command. The importer checks the recorded finding count and repository/ref
claims. It retains each original finding as JSON text so large IDs survive replay. The reader also accepts saved v1 project exports with native object rows and decimal type witnesses.
claims. It retains each original finding as JSON text so large IDs survive replay.

A finding that identifies its repository only by name can use the project name,
the Git URL path, or the final path component of either. For any other name,
Expand Down Expand Up @@ -483,18 +483,14 @@ and advisory IDs when Semgrep supplies them. With the same source key, importing
selection reports them as unchanged when switching between a selection and a full collection.
See the [Semgrep Findings API](https://docs.semgrep.dev/api-reference/v1/findingsservice/list-code-supply-chain-or-ai-powered-detection-findings).

When replaying v1 exports whose findings were stored as objects, the first
replay converts the stored finding to JSON text and increments its version.
Its local and source IDs stay the same.
For an older `semgrep-platform-findings/v1` export, rerun `cs import semgrep`
with the selected deployment, project, product and ref to collect a current export.
Check its IDs and scope before saving or publishing it.

## Replay saved Platform findings

Semgrep's native signed 64-bit finding IDs match across file and SDK imports.
For synthetic IDs above that range, repeat the original workflow and preserve
the ID's spelling and JSON type. A saved selection keeps its literal ref,
including older short refs. For a row without a URL but with a matching
project ID and ref, saved files also accept the complete percent-encoded
repository path. New selections require the decoded path.
Keep the original spelling and JSON type of each selected ID and the full Git ref in the saved capture.

**Repository mapping.** Local preview compares the project URL with the checkout's
named fetch URLs. For an IP address, use its canonical spelling, such as
Expand All @@ -513,14 +509,8 @@ evidence unchanged. Use a separate source key for CLI scans.
**Normalized SDK records.** Keep each generated `source_finding_id` with its
native row and original deployment, project, ref and product. If you assign
your own opaque ID, retain that scope in a marked Platform batch. The SDK checks
the serialized native row when present. In legacy v1 `source_data.finding`,
repository fields, `ref` (even `null`), recognized `product` or `issue_type`,
and non-null `found_dependency` or `vulnerability_identifier` can establish
scope. Put unrelated metadata under a different key, such as
`source_data.ticket`, or submit it in a batch without a Platform marker.
the serialized native row. Keep unrelated metadata in a separate field.

**Cloud retries.** Cloud checks the original project's repository, including
for Supply Chain findings without a typed repository field. New approvals
retain the collection scope of caller-managed rows. Older approvals check
the repository and the scope retained in their evidence. Retry the original
input with the same account and destination. [Retry a Cloud submission](schema-guide.md#retry-a-cloud-submission).
for Supply Chain findings without a typed repository field. Retry the original
selection with the same account and destination. [Retry a Cloud submission](schema-guide.md#retry-a-cloud-submission).
23 changes: 7 additions & 16 deletions docs/scanner-imports/snyk.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,17 +152,13 @@ The SDK's `collectSnykIssues` defaults to Open Source and returns the same repor
Passing the configured `SNYK_TOKEN` explicitly retains its configured regional
API origin. Pass independently verified `sourceRepositories` in its
dependencies when a private repository uses an SSH target. The offline reader
`readSnykFindings(path, "local")` accepts `snyk-rest-issues/v1` and
`snyk-rest-issues/v2` reports from Snyk REST API version `2026-03-25`.
The collector writes v2 and preserves the original project and issue evidence.
For open Snyk Code issues from an SCM integration, both versions import
repository-relative paths from open coordinates. V2 also imports each
coordinate's reported start line when available. V2 keeps URI and SSH addresses
in the original evidence and uses only repository file paths for code locations
and package manifests. For resolved issues, closed
coordinates, and projects monitored through the CLI or API, read the original
location from `evidence.source_data.issue` in v1 or from the JSON text in
`evidence.source_data.issueJson` in v2.
`readSnykFindings(path, "local")` accepts the collector's `snyk-rest-issues/v2`
report for Snyk REST API version `2026-03-25`. Run the collector again to replace
an archived v1 export. The report preserves the original project and issue
evidence. Open Snyk Code issues from an SCM integration import repository file
paths and reported start lines. For resolved issues, closed coordinates, and
CLI- or API-monitored projects, read the original location from
`evidence.source_data.issueJson`.

Use the same source key on later imports. The issue key within the API
origin, organization, project and product identifies the finding. Replaying the
Expand All @@ -179,11 +175,6 @@ the monitored project for a row that retains only its original issue. The Cloud
preview includes the verified project and any captured target in the evidence it
will publish.

For a v1 row, keep `source_data.issue` and the original `source_data.numericFields`
when present. If you trim the metadata, keep its matching entries for every
`issue`, `project`, and `target` object you retain. V1 uses those entries to
distinguish exact numbers from strings with the same spelling. For a v2 row,
keep `source_data.issueJson`.
The local workbench stores the verified project and target with that issue,
so the stored row can be imported again. All selected issues must share the
report’s API region, organization, project and product. Keep SCM-integrated
Expand Down
13 changes: 6 additions & 7 deletions sdk/typescript/docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1630,11 +1630,10 @@ Run the command with `--dry-run --format json`. Check `accountId`,
- **Private findings inbox.** If the preview includes `privateImport`, the
report goes to your inbox. The repository and connector identify the
suggested assessment target. Check `format`, `bytes` and `sha256` in
`privateImport` to identify the upload. For an Endor Finding, an Endor
`list.objects` response or a Wiz vulnerability JSON export, the CLI can
send the original bytes when Cloud accepts the format and every record
matches your selection. Other inputs, including arrays of selected Endor
Findings, use `security.findings.v1`.
`privateImport` to identify the upload. The CLI and SDK encode the reviewed
occurrences as `security.findings.v1` when it fits. For Wiz and Endor, an
inbox that requires native records or has a smaller upload limit can receive
native records that reproduce the reviewed evidence.
- **Repository publisher.** Check `repository.environment_id` in each entry
of `requests`. The publisher checks the current source versions. Existing
records keep their environment. New records use the repository's authorized
Expand Down Expand Up @@ -1663,10 +1662,10 @@ instructions. API requests always use the API root. The older
### Resume interrupted imports

If the result includes `savedSubmission`, keep that checkpoint. Rerun the
original command with `--dry-run --format json` using the same input, account,
original command with `--dry-run --format json` using the same build, input, account,
destination, API endpoint and state directory. Check `cloudApiUrl`. If
`resumed` is true, confirm to continue the saved submission. If it's false,
review the new submission before confirming.
review the new submission before confirming. Finish older-format pending submissions with the client that created them. If a response was lost, check the inbox job or repository records before starting a fresh upload.

- **Private inbox.** The CLI checks the report digest and reuses its saved
request IDs. It can read an accepted job even if the deployment has since
Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/docs/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ For normalized evidence, use `prepareNormalizedLocalImport(findings, options)`
or `prepareNormalizedExternalPublication(findings, options)` and review the
preview in the same way. Cloud file imports use `ExternalPublicationOptions`,
and normalized Cloud imports use `NormalizedExternalPublicationOptions`. Keep
Cloud receipts and saved requests when retrying an interrupted submission.
Cloud receipts and saved requests when retrying an interrupted submission. Use the SDK build that created a pending submission to finish it before upgrading.

Use `listLocalExternalFindings({ repository })` to read saved scanner evidence.
It starts unassessed. Reimporting the same repository, provider, source key
Expand Down
4 changes: 0 additions & 4 deletions sdk/typescript/scripts/package-dist-files.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const distModules = [
"external-findings-local",
"external-findings-import",
"external-findings-read",
"reader-selection",
"scanner-import",
"inspector-findings",
"continuum-findings",
Expand All @@ -42,7 +41,6 @@ const distModules = [
"checkmarx-xml",
"snyk-findings",
"snyk-rest-findings",
"snyk-rest-v1",
"snyk-token-origin",
"scanner-repository-url",
"semgrep-findings",
Expand All @@ -52,8 +50,6 @@ const distModules = [
"hosted-git-repository",
"semgrep-platform-collector",
"hosted-scanner-http",
"hosted-scanner-v1",
"semgrep-platform-v1",
"sonar-findings",
"sonar-collector",
"sonar-import",
Expand Down
53 changes: 8 additions & 45 deletions sdk/typescript/src/blackduck-findings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { canonicalJson, isRecord } from "./record.js";
import {
array,
normalizeRecords,
readScannerJson,
scannerJsonReader,
record,
reportableScannerSeverity,
requiredText,
Expand Down Expand Up @@ -357,7 +357,7 @@ function blackDuckPackages(row: Record<string, unknown>) {
? decodeURIComponent(purl[3])
: text(row["componentVersionName"]),
};
// Earlier versions used the origin fallback for owner/repository PURLs.
// Retain the origin coordinate if the qualified owner/repository PURL exceeds a Cloud limit.
return ecosystem === "github" && name.includes("/")
? { packages: fallback, enrichment: { packages: [pkg] } }
: { packages: [pkg] };
Expand All @@ -370,11 +370,10 @@ function blackDuckPackages(row: Record<string, unknown>) {
return { packages: fallback };
}

function normalizeReport(
export function normalizeBlackDuckFindings(
input: unknown,
sourceRepositoryUrl: string,
target: ExternalImportTarget,
previous: boolean,
target: ExternalImportTarget = "cloud",
): ExternalFindings {
const repository = declaredScannerRepositoryUrl(
sourceRepositoryUrl,
Expand Down Expand Up @@ -435,7 +434,7 @@ function normalizeReport(
},
},
target,
previous ? undefined : enrichment,
enrichment,
);
},
(entry) => occurrenceId(entry, projectVersion),
Expand All @@ -459,42 +458,6 @@ function normalizeReport(
return result;
}

export function normalizeBlackDuckFindings(
input: unknown,
sourceRepositoryUrl: string,
target: ExternalImportTarget = "cloud",
): ExternalFindings {
return normalizeReport(input, sourceRepositoryUrl, target, false);
}

/** @internal Earlier readers used the native origin fallback for GitHub package URLs. */
export function needsPreviousBlackDuckProjection({
findings,
}: ExternalFindings) {
return findings.some(({ evidence }) =>
evidence.packages?.some(
(pkg) => pkg.ecosystem === "github" && pkg.name.includes("/"),
),
);
}

/** @internal Reconstruct the original evidence for an interrupted Cloud request. */
export function normalizePreviousBlackDuckFindings(
input: unknown,
sourceRepositoryUrl: string,
target: ExternalImportTarget = "cloud",
): ExternalFindings {
return normalizeReport(input, sourceRepositoryUrl, target, true);
}

export async function readBlackDuckFindings(
path: string,
sourceRepositoryUrl: string,
target: ExternalImportTarget = "cloud",
): Promise<ExternalFindings> {
return normalizeBlackDuckFindings(
await readScannerJson(path),
sourceRepositoryUrl,
target,
);
}
export const readBlackDuckFindings = scannerJsonReader(
normalizeBlackDuckFindings,
);
Loading
Loading