update
This commit is contained in:
13
package/node_modules/libnpmpublish/LICENSE
generated
vendored
Normal file
13
package/node_modules/libnpmpublish/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright npm, Inc
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
120
package/node_modules/libnpmpublish/README.md
generated
vendored
Normal file
120
package/node_modules/libnpmpublish/README.md
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
# libnpmpublish
|
||||
|
||||
[](https://npm.im/libnpmpublish)
|
||||
[](https://npm.im/libnpmpublish)
|
||||
[](https://github.com/npm/cli/actions/workflows/ci-libnpmpublish.yml)
|
||||
|
||||
[`libnpmpublish`](https://github.com/npm/libnpmpublish) is a Node.js
|
||||
library for programmatically publishing and unpublishing npm packages. Give
|
||||
it a manifest as an object and a tarball as a Buffer, and it'll put them on
|
||||
the registry for you.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Example](#example)
|
||||
* [Install](#install)
|
||||
* [API](#api)
|
||||
* [publish/unpublish opts](#opts)
|
||||
* [`publish()`](#publish)
|
||||
* [`unpublish()`](#unpublish)
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { publish, unpublish } = require('libnpmpublish')
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
`$ npm install libnpmpublish`
|
||||
|
||||
### API
|
||||
|
||||
#### <a name="opts"></a> `opts` for `libnpmpublish` commands
|
||||
|
||||
`libnpmpublish` uses
|
||||
[`npm-registry-fetch`](https://npm.im/npm-registry-fetch). Most options
|
||||
are passed through directly to that library, so please refer to [its own
|
||||
`opts` documentation](http://npm.im/npm-registry-fetch#fetch-options) for
|
||||
options that can be passed in.
|
||||
|
||||
A couple of options of note:
|
||||
|
||||
* `opts.defaultTag` - registers the published package with the given tag,
|
||||
defaults to `latest`.
|
||||
|
||||
* `opts.access` - tells the registry whether this package should be
|
||||
published as `public` or `restricted`. Only applies to scoped
|
||||
packages. Defaults to `public`.
|
||||
|
||||
* `opts.token` - can be passed in and will be used as the authentication
|
||||
token for the registry. For other ways to pass in auth details, see the
|
||||
n-r-f docs.
|
||||
|
||||
* `opts.provenance` - when running in a supported CI environment, will trigger
|
||||
the generation of a signed provenance statement to be published alongside
|
||||
the package. Mutually exclusive with the `provenanceFile` option.
|
||||
|
||||
* `opts.provenanceFile` - specifies the path to an externally-generated
|
||||
provenance statement to be published alongside the package. Mutually
|
||||
exclusive with the `provenance` option. The specified file should be a
|
||||
[Sigstore Bundle](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto)
|
||||
containing a [DSSE](https://github.com/secure-systems-lab/dsse)-packaged
|
||||
provenance statement.
|
||||
|
||||
#### <a name="publish"></a> `> libpub.publish(manifest, tarData, [opts]) -> Promise`
|
||||
|
||||
Sends the package represented by the `manifest` and `tarData` to the
|
||||
configured registry.
|
||||
|
||||
`manifest` should be the parsed `package.json` for the package being
|
||||
published (which can also be the manifest pulled from a packument, a git
|
||||
repo, tarball, etc.)
|
||||
|
||||
`tarData` is a `Buffer` of the tarball being published.
|
||||
|
||||
If `opts.npmVersion` is passed in, it will be used as the `_npmVersion`
|
||||
field in the outgoing packument. You may put your own user-agent string in
|
||||
there to identify your publishes.
|
||||
|
||||
If `opts.algorithms` is passed in, it should be an array of hashing
|
||||
algorithms to generate `integrity` hashes for. The default is `['sha512']`,
|
||||
which means you end up with `dist.integrity = 'sha512-deadbeefbadc0ffee'`.
|
||||
Any algorithm supported by your current node version is allowed -- npm
|
||||
clients that do not support those algorithms will simply ignore the
|
||||
unsupported hashes.
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
// note that pacote.manifest() and pacote.tarball() can also take
|
||||
// any spec that npm can install. a folder shown here, since that's
|
||||
// far and away the most common use case.
|
||||
const path = '/a/path/to/your/source/code'
|
||||
const pacote = require('pacote') // see: http://npm.im/pacote
|
||||
const manifest = await pacote.manifest(path)
|
||||
const tarData = await pacote.tarball(path)
|
||||
await libpub.publish(manifest, tarData, {
|
||||
npmVersion: 'my-pub-script@1.0.2',
|
||||
token: 'my-auth-token-here'
|
||||
}, opts)
|
||||
// Package has been published to the npm registry.
|
||||
```
|
||||
|
||||
#### <a name="unpublish"></a> `> libpub.unpublish(spec, [opts]) -> Promise`
|
||||
|
||||
Unpublishes `spec` from the appropriate registry. The registry in question may
|
||||
have its own limitations on unpublishing.
|
||||
|
||||
`spec` should be either a string, or a valid
|
||||
[`npm-package-arg`](https://npm.im/npm-package-arg) parsed spec object. For
|
||||
legacy compatibility reasons, only `tag` and `version` specs will work as
|
||||
expected. `range` specs will fail silently in most cases.
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
await libpub.unpublish('lodash', { token: 'i-am-the-worst'})
|
||||
//
|
||||
// `lodash` has now been unpublished, along with all its versions
|
||||
```
|
4
package/node_modules/libnpmpublish/lib/index.js
generated
vendored
Normal file
4
package/node_modules/libnpmpublish/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
publish: require('./publish.js'),
|
||||
unpublish: require('./unpublish.js'),
|
||||
}
|
245
package/node_modules/libnpmpublish/lib/provenance.js
generated
vendored
Normal file
245
package/node_modules/libnpmpublish/lib/provenance.js
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
const sigstore = require('sigstore')
|
||||
const { readFile } = require('node:fs/promises')
|
||||
const ci = require('ci-info')
|
||||
const { env } = process
|
||||
|
||||
const INTOTO_PAYLOAD_TYPE = 'application/vnd.in-toto+json'
|
||||
const INTOTO_STATEMENT_V01_TYPE = 'https://in-toto.io/Statement/v0.1'
|
||||
const INTOTO_STATEMENT_V1_TYPE = 'https://in-toto.io/Statement/v1'
|
||||
const SLSA_PREDICATE_V02_TYPE = 'https://slsa.dev/provenance/v0.2'
|
||||
const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
|
||||
|
||||
const GITHUB_BUILDER_ID_PREFIX = 'https://github.com/actions/runner'
|
||||
const GITHUB_BUILD_TYPE = 'https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1'
|
||||
|
||||
const GITLAB_BUILD_TYPE_PREFIX = 'https://github.com/npm/cli/gitlab'
|
||||
const GITLAB_BUILD_TYPE_VERSION = 'v0alpha1'
|
||||
|
||||
const generateProvenance = async (subject, opts) => {
|
||||
let payload
|
||||
if (ci.GITHUB_ACTIONS) {
|
||||
/* istanbul ignore next - not covering missing env var case */
|
||||
const relativeRef = (env.GITHUB_WORKFLOW_REF || '').replace(env.GITHUB_REPOSITORY + '/', '')
|
||||
const delimiterIndex = relativeRef.indexOf('@')
|
||||
const workflowPath = relativeRef.slice(0, delimiterIndex)
|
||||
const workflowRef = relativeRef.slice(delimiterIndex + 1)
|
||||
|
||||
payload = {
|
||||
_type: INTOTO_STATEMENT_V1_TYPE,
|
||||
subject,
|
||||
predicateType: SLSA_PREDICATE_V1_TYPE,
|
||||
predicate: {
|
||||
buildDefinition: {
|
||||
buildType: GITHUB_BUILD_TYPE,
|
||||
externalParameters: {
|
||||
workflow: {
|
||||
ref: workflowRef,
|
||||
repository: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}`,
|
||||
path: workflowPath,
|
||||
},
|
||||
},
|
||||
internalParameters: {
|
||||
github: {
|
||||
event_name: env.GITHUB_EVENT_NAME,
|
||||
repository_id: env.GITHUB_REPOSITORY_ID,
|
||||
repository_owner_id: env.GITHUB_REPOSITORY_OWNER_ID,
|
||||
},
|
||||
},
|
||||
resolvedDependencies: [
|
||||
{
|
||||
uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`,
|
||||
digest: {
|
||||
gitCommit: env.GITHUB_SHA,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
runDetails: {
|
||||
builder: { id: `${GITHUB_BUILDER_ID_PREFIX}/${env.RUNNER_ENVIRONMENT}` },
|
||||
metadata: {
|
||||
/* eslint-disable-next-line max-len */
|
||||
invocationId: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}/attempts/${env.GITHUB_RUN_ATTEMPT}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
if (ci.GITLAB) {
|
||||
payload = {
|
||||
_type: INTOTO_STATEMENT_V01_TYPE,
|
||||
subject,
|
||||
predicateType: SLSA_PREDICATE_V02_TYPE,
|
||||
predicate: {
|
||||
buildType: `${GITLAB_BUILD_TYPE_PREFIX}/${GITLAB_BUILD_TYPE_VERSION}`,
|
||||
builder: { id: `${env.CI_PROJECT_URL}/-/runners/${env.CI_RUNNER_ID}` },
|
||||
invocation: {
|
||||
configSource: {
|
||||
uri: `git+${env.CI_PROJECT_URL}`,
|
||||
digest: {
|
||||
sha1: env.CI_COMMIT_SHA,
|
||||
},
|
||||
entryPoint: env.CI_JOB_NAME,
|
||||
},
|
||||
parameters: {
|
||||
CI: env.CI,
|
||||
CI_API_GRAPHQL_URL: env.CI_API_GRAPHQL_URL,
|
||||
CI_API_V4_URL: env.CI_API_V4_URL,
|
||||
CI_BUILD_BEFORE_SHA: env.CI_BUILD_BEFORE_SHA,
|
||||
CI_BUILD_ID: env.CI_BUILD_ID,
|
||||
CI_BUILD_NAME: env.CI_BUILD_NAME,
|
||||
CI_BUILD_REF: env.CI_BUILD_REF,
|
||||
CI_BUILD_REF_NAME: env.CI_BUILD_REF_NAME,
|
||||
CI_BUILD_REF_SLUG: env.CI_BUILD_REF_SLUG,
|
||||
CI_BUILD_STAGE: env.CI_BUILD_STAGE,
|
||||
CI_COMMIT_BEFORE_SHA: env.CI_COMMIT_BEFORE_SHA,
|
||||
CI_COMMIT_BRANCH: env.CI_COMMIT_BRANCH,
|
||||
CI_COMMIT_REF_NAME: env.CI_COMMIT_REF_NAME,
|
||||
CI_COMMIT_REF_PROTECTED: env.CI_COMMIT_REF_PROTECTED,
|
||||
CI_COMMIT_REF_SLUG: env.CI_COMMIT_REF_SLUG,
|
||||
CI_COMMIT_SHA: env.CI_COMMIT_SHA,
|
||||
CI_COMMIT_SHORT_SHA: env.CI_COMMIT_SHORT_SHA,
|
||||
CI_COMMIT_TIMESTAMP: env.CI_COMMIT_TIMESTAMP,
|
||||
CI_COMMIT_TITLE: env.CI_COMMIT_TITLE,
|
||||
CI_CONFIG_PATH: env.CI_CONFIG_PATH,
|
||||
CI_DEFAULT_BRANCH: env.CI_DEFAULT_BRANCH,
|
||||
CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX:
|
||||
env.CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX,
|
||||
CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX: env.CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX,
|
||||
CI_DEPENDENCY_PROXY_SERVER: env.CI_DEPENDENCY_PROXY_SERVER,
|
||||
CI_DEPENDENCY_PROXY_USER: env.CI_DEPENDENCY_PROXY_USER,
|
||||
CI_JOB_ID: env.CI_JOB_ID,
|
||||
CI_JOB_NAME: env.CI_JOB_NAME,
|
||||
CI_JOB_NAME_SLUG: env.CI_JOB_NAME_SLUG,
|
||||
CI_JOB_STAGE: env.CI_JOB_STAGE,
|
||||
CI_JOB_STARTED_AT: env.CI_JOB_STARTED_AT,
|
||||
CI_JOB_URL: env.CI_JOB_URL,
|
||||
CI_NODE_TOTAL: env.CI_NODE_TOTAL,
|
||||
CI_PAGES_DOMAIN: env.CI_PAGES_DOMAIN,
|
||||
CI_PAGES_URL: env.CI_PAGES_URL,
|
||||
CI_PIPELINE_CREATED_AT: env.CI_PIPELINE_CREATED_AT,
|
||||
CI_PIPELINE_ID: env.CI_PIPELINE_ID,
|
||||
CI_PIPELINE_IID: env.CI_PIPELINE_IID,
|
||||
CI_PIPELINE_SOURCE: env.CI_PIPELINE_SOURCE,
|
||||
CI_PIPELINE_URL: env.CI_PIPELINE_URL,
|
||||
CI_PROJECT_CLASSIFICATION_LABEL: env.CI_PROJECT_CLASSIFICATION_LABEL,
|
||||
CI_PROJECT_DESCRIPTION: env.CI_PROJECT_DESCRIPTION,
|
||||
CI_PROJECT_ID: env.CI_PROJECT_ID,
|
||||
CI_PROJECT_NAME: env.CI_PROJECT_NAME,
|
||||
CI_PROJECT_NAMESPACE: env.CI_PROJECT_NAMESPACE,
|
||||
CI_PROJECT_NAMESPACE_ID: env.CI_PROJECT_NAMESPACE_ID,
|
||||
CI_PROJECT_PATH: env.CI_PROJECT_PATH,
|
||||
CI_PROJECT_PATH_SLUG: env.CI_PROJECT_PATH_SLUG,
|
||||
CI_PROJECT_REPOSITORY_LANGUAGES: env.CI_PROJECT_REPOSITORY_LANGUAGES,
|
||||
CI_PROJECT_ROOT_NAMESPACE: env.CI_PROJECT_ROOT_NAMESPACE,
|
||||
CI_PROJECT_TITLE: env.CI_PROJECT_TITLE,
|
||||
CI_PROJECT_URL: env.CI_PROJECT_URL,
|
||||
CI_PROJECT_VISIBILITY: env.CI_PROJECT_VISIBILITY,
|
||||
CI_REGISTRY: env.CI_REGISTRY,
|
||||
CI_REGISTRY_IMAGE: env.CI_REGISTRY_IMAGE,
|
||||
CI_REGISTRY_USER: env.CI_REGISTRY_USER,
|
||||
CI_RUNNER_DESCRIPTION: env.CI_RUNNER_DESCRIPTION,
|
||||
CI_RUNNER_ID: env.CI_RUNNER_ID,
|
||||
CI_RUNNER_TAGS: env.CI_RUNNER_TAGS,
|
||||
CI_SERVER_HOST: env.CI_SERVER_HOST,
|
||||
CI_SERVER_NAME: env.CI_SERVER_NAME,
|
||||
CI_SERVER_PORT: env.CI_SERVER_PORT,
|
||||
CI_SERVER_PROTOCOL: env.CI_SERVER_PROTOCOL,
|
||||
CI_SERVER_REVISION: env.CI_SERVER_REVISION,
|
||||
CI_SERVER_SHELL_SSH_HOST: env.CI_SERVER_SHELL_SSH_HOST,
|
||||
CI_SERVER_SHELL_SSH_PORT: env.CI_SERVER_SHELL_SSH_PORT,
|
||||
CI_SERVER_URL: env.CI_SERVER_URL,
|
||||
CI_SERVER_VERSION: env.CI_SERVER_VERSION,
|
||||
CI_SERVER_VERSION_MAJOR: env.CI_SERVER_VERSION_MAJOR,
|
||||
CI_SERVER_VERSION_MINOR: env.CI_SERVER_VERSION_MINOR,
|
||||
CI_SERVER_VERSION_PATCH: env.CI_SERVER_VERSION_PATCH,
|
||||
CI_TEMPLATE_REGISTRY_HOST: env.CI_TEMPLATE_REGISTRY_HOST,
|
||||
GITLAB_CI: env.GITLAB_CI,
|
||||
GITLAB_FEATURES: env.GITLAB_FEATURES,
|
||||
GITLAB_USER_ID: env.GITLAB_USER_ID,
|
||||
GITLAB_USER_LOGIN: env.GITLAB_USER_LOGIN,
|
||||
RUNNER_GENERATE_ARTIFACTS_METADATA: env.RUNNER_GENERATE_ARTIFACTS_METADATA,
|
||||
},
|
||||
environment: {
|
||||
name: env.CI_RUNNER_DESCRIPTION,
|
||||
architecture: env.CI_RUNNER_EXECUTABLE_ARCH,
|
||||
server: env.CI_SERVER_URL,
|
||||
project: env.CI_PROJECT_PATH,
|
||||
job: {
|
||||
id: env.CI_JOB_ID,
|
||||
},
|
||||
pipeline: {
|
||||
id: env.CI_PIPELINE_ID,
|
||||
ref: env.CI_CONFIG_PATH,
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
buildInvocationId: `${env.CI_JOB_URL}`,
|
||||
completeness: {
|
||||
parameters: true,
|
||||
environment: true,
|
||||
materials: false,
|
||||
},
|
||||
reproducible: false,
|
||||
},
|
||||
materials: [
|
||||
{
|
||||
uri: `git+${env.CI_PROJECT_URL}`,
|
||||
digest: {
|
||||
sha1: env.CI_COMMIT_SHA,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
return sigstore.attest(Buffer.from(JSON.stringify(payload)), INTOTO_PAYLOAD_TYPE, opts)
|
||||
}
|
||||
|
||||
const verifyProvenance = async (subject, provenancePath) => {
|
||||
let provenanceBundle
|
||||
try {
|
||||
provenanceBundle = JSON.parse(await readFile(provenancePath))
|
||||
} catch (err) {
|
||||
err.message = `Invalid provenance provided: ${err.message}`
|
||||
throw err
|
||||
}
|
||||
|
||||
const payload = extractProvenance(provenanceBundle)
|
||||
if (!payload.subject || !payload.subject.length) {
|
||||
throw new Error('No subject found in sigstore bundle payload')
|
||||
}
|
||||
if (payload.subject.length > 1) {
|
||||
throw new Error('Found more than one subject in the sigstore bundle payload')
|
||||
}
|
||||
|
||||
const bundleSubject = payload.subject[0]
|
||||
if (subject.name !== bundleSubject.name) {
|
||||
throw new Error(
|
||||
`Provenance subject ${bundleSubject.name} does not match the package: ${subject.name}`
|
||||
)
|
||||
}
|
||||
if (subject.digest.sha512 !== bundleSubject.digest.sha512) {
|
||||
throw new Error('Provenance subject digest does not match the package')
|
||||
}
|
||||
|
||||
await sigstore.verify(provenanceBundle)
|
||||
return provenanceBundle
|
||||
}
|
||||
|
||||
const extractProvenance = (bundle) => {
|
||||
if (!bundle?.dsseEnvelope?.payload) {
|
||||
throw new Error('No dsseEnvelope with payload found in sigstore bundle')
|
||||
}
|
||||
try {
|
||||
return JSON.parse(Buffer.from(bundle.dsseEnvelope.payload, 'base64').toString('utf8'))
|
||||
} catch (err) {
|
||||
err.message = `Failed to parse payload from dsseEnvelope: ${err.message}`
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateProvenance,
|
||||
verifyProvenance,
|
||||
}
|
225
package/node_modules/libnpmpublish/lib/publish.js
generated
vendored
Normal file
225
package/node_modules/libnpmpublish/lib/publish.js
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
const { fixer } = require('normalize-package-data')
|
||||
const npmFetch = require('npm-registry-fetch')
|
||||
const npa = require('npm-package-arg')
|
||||
const { log } = require('proc-log')
|
||||
const semver = require('semver')
|
||||
const { URL } = require('node:url')
|
||||
const ssri = require('ssri')
|
||||
const ciInfo = require('ci-info')
|
||||
|
||||
const { generateProvenance, verifyProvenance } = require('./provenance')
|
||||
|
||||
const TLOG_BASE_URL = 'https://search.sigstore.dev/'
|
||||
|
||||
const publish = async (manifest, tarballData, opts) => {
|
||||
if (manifest.private) {
|
||||
throw Object.assign(
|
||||
new Error(`This package has been marked as private
|
||||
Remove the 'private' field from the package.json to publish it.`),
|
||||
{ code: 'EPRIVATE' }
|
||||
)
|
||||
}
|
||||
|
||||
// spec is used to pick the appropriate registry/auth combo
|
||||
const spec = npa.resolve(manifest.name, manifest.version)
|
||||
opts = {
|
||||
access: 'public',
|
||||
algorithms: ['sha512'],
|
||||
defaultTag: 'latest',
|
||||
...opts,
|
||||
spec,
|
||||
}
|
||||
|
||||
const reg = npmFetch.pickRegistry(spec, opts)
|
||||
const pubManifest = patchManifest(manifest, opts)
|
||||
|
||||
// registry-frontdoor cares about the access level,
|
||||
// which is only configurable for scoped packages
|
||||
if (!spec.scope && opts.access === 'restricted') {
|
||||
throw Object.assign(
|
||||
new Error("Can't restrict access to unscoped packages."),
|
||||
{ code: 'EUNSCOPED' }
|
||||
)
|
||||
}
|
||||
|
||||
const { metadata, transparencyLogUrl } = await buildMetadata(
|
||||
reg,
|
||||
pubManifest,
|
||||
tarballData,
|
||||
spec,
|
||||
opts
|
||||
)
|
||||
|
||||
const res = await npmFetch(spec.escapedName, {
|
||||
...opts,
|
||||
method: 'PUT',
|
||||
body: metadata,
|
||||
ignoreBody: true,
|
||||
})
|
||||
if (transparencyLogUrl) {
|
||||
res.transparencyLogUrl = transparencyLogUrl
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const patchManifest = (_manifest, opts) => {
|
||||
const { npmVersion } = opts
|
||||
// we only update top-level fields, so a shallow clone is fine
|
||||
const manifest = { ..._manifest }
|
||||
|
||||
manifest._nodeVersion = process.versions.node
|
||||
if (npmVersion) {
|
||||
manifest._npmVersion = npmVersion
|
||||
}
|
||||
|
||||
fixer.fixNameField(manifest, { strict: true, allowLegacyCase: true })
|
||||
const version = semver.clean(manifest.version)
|
||||
if (!version) {
|
||||
throw Object.assign(
|
||||
new Error('invalid semver: ' + manifest.version),
|
||||
{ code: 'EBADSEMVER' }
|
||||
)
|
||||
}
|
||||
manifest.version = version
|
||||
return manifest
|
||||
}
|
||||
|
||||
const buildMetadata = async (registry, manifest, tarballData, spec, opts) => {
|
||||
const { access, defaultTag, algorithms, provenance, provenanceFile } = opts
|
||||
const root = {
|
||||
_id: manifest.name,
|
||||
name: manifest.name,
|
||||
description: manifest.description,
|
||||
'dist-tags': {},
|
||||
versions: {},
|
||||
access,
|
||||
}
|
||||
|
||||
root.versions[manifest.version] = manifest
|
||||
const tag = manifest.tag || defaultTag
|
||||
root['dist-tags'][tag] = manifest.version
|
||||
|
||||
const tarballName = `${manifest.name}-${manifest.version}.tgz`
|
||||
const provenanceBundleName = `${manifest.name}-${manifest.version}.sigstore`
|
||||
const tarballURI = `${manifest.name}/-/${tarballName}`
|
||||
const integrity = ssri.fromData(tarballData, {
|
||||
algorithms: [...new Set(['sha1'].concat(algorithms))],
|
||||
})
|
||||
|
||||
manifest._id = `${manifest.name}@${manifest.version}`
|
||||
manifest.dist = { ...manifest.dist }
|
||||
// Don't bother having sha1 in the actual integrity field
|
||||
manifest.dist.integrity = integrity.sha512[0].toString()
|
||||
// Legacy shasum support
|
||||
manifest.dist.shasum = integrity.sha1[0].hexDigest()
|
||||
|
||||
// NB: the CLI always fetches via HTTPS if the registry is HTTPS,
|
||||
// regardless of what's here. This makes it so that installing
|
||||
// from an HTTP-only mirror doesn't cause problems, though.
|
||||
manifest.dist.tarball = new URL(tarballURI, registry).href
|
||||
.replace(/^https:\/\//, 'http://')
|
||||
|
||||
root._attachments = {}
|
||||
root._attachments[tarballName] = {
|
||||
content_type: 'application/octet-stream',
|
||||
data: tarballData.toString('base64'),
|
||||
length: tarballData.length,
|
||||
}
|
||||
|
||||
// Handle case where --provenance flag was set to true
|
||||
let transparencyLogUrl
|
||||
if (provenance === true || provenanceFile) {
|
||||
let provenanceBundle
|
||||
const subject = {
|
||||
name: npa.toPurl(spec),
|
||||
digest: { sha512: integrity.sha512[0].hexDigest() },
|
||||
}
|
||||
|
||||
if (provenance === true) {
|
||||
await ensureProvenanceGeneration(registry, spec, opts)
|
||||
provenanceBundle = await generateProvenance([subject], opts)
|
||||
|
||||
/* eslint-disable-next-line max-len */
|
||||
log.notice('publish', `Signed provenance statement with source and build information from ${ciInfo.name}`)
|
||||
|
||||
const tlogEntry = provenanceBundle?.verificationMaterial?.tlogEntries[0]
|
||||
/* istanbul ignore else */
|
||||
if (tlogEntry) {
|
||||
transparencyLogUrl = `${TLOG_BASE_URL}?logIndex=${tlogEntry.logIndex}`
|
||||
log.notice(
|
||||
'publish',
|
||||
`Provenance statement published to transparency log: ${transparencyLogUrl}`
|
||||
)
|
||||
}
|
||||
} else {
|
||||
provenanceBundle = await verifyProvenance(subject, provenanceFile)
|
||||
}
|
||||
|
||||
const serializedBundle = JSON.stringify(provenanceBundle)
|
||||
root._attachments[provenanceBundleName] = {
|
||||
content_type: provenanceBundle.mediaType,
|
||||
data: serializedBundle,
|
||||
length: serializedBundle.length,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
metadata: root,
|
||||
transparencyLogUrl,
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all the prereqs are met for provenance generation
|
||||
const ensureProvenanceGeneration = async (registry, spec, opts) => {
|
||||
if (ciInfo.GITHUB_ACTIONS) {
|
||||
// Ensure that the GHA OIDC token is available
|
||||
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
|
||||
throw Object.assign(
|
||||
/* eslint-disable-next-line max-len */
|
||||
new Error('Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'),
|
||||
{ code: 'EUSAGE' }
|
||||
)
|
||||
}
|
||||
} else if (ciInfo.GITLAB) {
|
||||
// Ensure that the Sigstore OIDC token is available
|
||||
if (!process.env.SIGSTORE_ID_TOKEN) {
|
||||
throw Object.assign(
|
||||
/* eslint-disable-next-line max-len */
|
||||
new Error('Provenance generation in GitLab CI requires "SIGSTORE_ID_TOKEN" with "sigstore" audience to be present in "id_tokens". For more info see:\nhttps://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html'),
|
||||
{ code: 'EUSAGE' }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw Object.assign(
|
||||
new Error('Automatic provenance generation not supported for provider: ' + ciInfo.name),
|
||||
{ code: 'EUSAGE' }
|
||||
)
|
||||
}
|
||||
|
||||
// Some registries (e.g. GH packages) require auth to check visibility,
|
||||
// and always return 404 when no auth is supplied. In this case we assume
|
||||
// the package is always private and require `--access public` to publish
|
||||
// with provenance.
|
||||
let visibility = { public: false }
|
||||
if (opts.access !== 'public') {
|
||||
try {
|
||||
const res = await npmFetch
|
||||
.json(`${registry}/-/package/${spec.escapedName}/visibility`, opts)
|
||||
visibility = res
|
||||
} catch (err) {
|
||||
if (err.code !== 'E404') {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!visibility.public && opts.provenance === true && opts.access !== 'public') {
|
||||
throw Object.assign(
|
||||
/* eslint-disable-next-line max-len */
|
||||
new Error("Can't generate provenance for new or private package, you must set `access` to public."),
|
||||
{ code: 'EUSAGE' }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = publish
|
119
package/node_modules/libnpmpublish/lib/unpublish.js
generated
vendored
Normal file
119
package/node_modules/libnpmpublish/lib/unpublish.js
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
'use strict'
|
||||
|
||||
const { URL } = require('node:url')
|
||||
const npa = require('npm-package-arg')
|
||||
const npmFetch = require('npm-registry-fetch')
|
||||
const semver = require('semver')
|
||||
|
||||
// given a tarball url and a registry url, returns just the
|
||||
// relevant pathname portion of it, so that it can be handled
|
||||
// elegantly by npm-registry-fetch which only expects pathnames
|
||||
// and handles the registry hostname via opts
|
||||
const getPathname = (tarball, registry) => {
|
||||
const registryUrl = new URL(registry).pathname.slice(1)
|
||||
let tarballUrl = new URL(tarball).pathname.slice(1)
|
||||
|
||||
// test the tarball url to see if it starts with a possible
|
||||
// pathname from the registry url, in that case strips that portion
|
||||
// of it so that we only return the post-registry-url pathname
|
||||
if (registryUrl) {
|
||||
tarballUrl = tarballUrl.slice(registryUrl.length)
|
||||
}
|
||||
return tarballUrl
|
||||
}
|
||||
|
||||
const unpublish = async (spec, opts) => {
|
||||
spec = npa(spec)
|
||||
// spec is used to pick the appropriate registry/auth combo.
|
||||
opts = {
|
||||
force: false,
|
||||
...opts,
|
||||
spec,
|
||||
}
|
||||
|
||||
try {
|
||||
const pkgUri = spec.escapedName
|
||||
const pkg = await npmFetch.json(pkgUri, {
|
||||
...opts,
|
||||
query: { write: true },
|
||||
})
|
||||
|
||||
const version = spec.rawSpec
|
||||
const allVersions = pkg.versions || {}
|
||||
const versionData = allVersions[version]
|
||||
|
||||
const rawSpecs = (!spec.rawSpec || spec.rawSpec === '*')
|
||||
const onlyVersion = Object.keys(allVersions).length === 1
|
||||
const noVersions = !Object.keys(allVersions).length
|
||||
|
||||
// if missing specific version,
|
||||
// assumed unpublished
|
||||
if (!versionData && !rawSpecs && !noVersions) {
|
||||
return true
|
||||
}
|
||||
|
||||
// unpublish all versions of a package:
|
||||
// - no specs supplied "npm unpublish foo"
|
||||
// - all specs ("*") "npm unpublish foo@*"
|
||||
// - there was only one version
|
||||
// - has no versions field on packument
|
||||
if (rawSpecs || onlyVersion || noVersions) {
|
||||
await npmFetch(`${pkgUri}/-rev/${pkg._rev}`, {
|
||||
...opts,
|
||||
method: 'DELETE',
|
||||
ignoreBody: true,
|
||||
})
|
||||
return true
|
||||
} else {
|
||||
const dist = allVersions[version].dist
|
||||
delete allVersions[version]
|
||||
|
||||
const latestVer = pkg['dist-tags'].latest
|
||||
|
||||
// deleting dist tags associated to version
|
||||
Object.keys(pkg['dist-tags']).forEach(tag => {
|
||||
if (pkg['dist-tags'][tag] === version) {
|
||||
delete pkg['dist-tags'][tag]
|
||||
}
|
||||
})
|
||||
|
||||
if (latestVer === version) {
|
||||
pkg['dist-tags'].latest = Object.keys(
|
||||
allVersions
|
||||
).sort(semver.compareLoose).pop()
|
||||
}
|
||||
|
||||
delete pkg._revisions
|
||||
delete pkg._attachments
|
||||
|
||||
// Update packument with removed versions
|
||||
await npmFetch(`${pkgUri}/-rev/${pkg._rev}`, {
|
||||
...opts,
|
||||
method: 'PUT',
|
||||
body: pkg,
|
||||
ignoreBody: true,
|
||||
})
|
||||
|
||||
// Remove the tarball itself
|
||||
const { _rev } = await npmFetch.json(pkgUri, {
|
||||
...opts,
|
||||
query: { write: true },
|
||||
})
|
||||
const tarballUrl = getPathname(dist.tarball, opts.registry)
|
||||
await npmFetch(`${tarballUrl}/-rev/${_rev}`, {
|
||||
...opts,
|
||||
method: 'DELETE',
|
||||
ignoreBody: true,
|
||||
})
|
||||
return true
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code !== 'E404') {
|
||||
throw err
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = unpublish
|
64
package/node_modules/libnpmpublish/package.json
generated
vendored
Normal file
64
package/node_modules/libnpmpublish/package.json
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "libnpmpublish",
|
||||
"version": "9.0.9",
|
||||
"description": "Programmatic API for the bits behind npm publish and unpublish",
|
||||
"author": "GitHub Inc.",
|
||||
"main": "lib/index.js",
|
||||
"contributors": [
|
||||
"Kat Marchán <kzm@zkat.tech>",
|
||||
"Claudia Hernández <claudia@npmjs.com>"
|
||||
],
|
||||
"files": [
|
||||
"bin/",
|
||||
"lib/"
|
||||
],
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
|
||||
"lintfix": "npm run lint -- --fix",
|
||||
"test": "tap",
|
||||
"posttest": "npm run lint",
|
||||
"postlint": "template-oss-check",
|
||||
"snap": "tap",
|
||||
"template-oss-apply": "template-oss-apply --force"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@npmcli/eslint-config": "^4.0.0",
|
||||
"@npmcli/mock-globals": "^1.0.0",
|
||||
"@npmcli/mock-registry": "^1.0.0",
|
||||
"@npmcli/template-oss": "4.22.0",
|
||||
"nock": "^13.3.3",
|
||||
"tap": "^16.3.8"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/npm/cli.git",
|
||||
"directory": "workspaces/libnpmpublish"
|
||||
},
|
||||
"bugs": "https://github.com/npm/cli/issues",
|
||||
"homepage": "https://npmjs.com/package/libnpmpublish",
|
||||
"dependencies": {
|
||||
"ci-info": "^4.0.0",
|
||||
"normalize-package-data": "^6.0.1",
|
||||
"npm-package-arg": "^11.0.2",
|
||||
"npm-registry-fetch": "^17.0.1",
|
||||
"proc-log": "^4.2.0",
|
||||
"semver": "^7.3.7",
|
||||
"sigstore": "^2.2.0",
|
||||
"ssri": "^10.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
},
|
||||
"templateOSS": {
|
||||
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
||||
"version": "4.22.0",
|
||||
"content": "../../scripts/template-oss/index.js"
|
||||
},
|
||||
"tap": {
|
||||
"nyc-arg": [
|
||||
"--exclude",
|
||||
"tap-snapshots/**"
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user