From 1947dce5ba0a3ea8fdfa2a60da12f0a355a8f603 Mon Sep 17 00:00:00 2001 From: Mattt Date: Mon, 5 Apr 2021 04:35:07 -0700 Subject: [PATCH 01/10] Fix regular expression for package scopes --- proposals/0292-package-registry-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 594211f99b..e00c077502 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -149,7 +149,7 @@ The maximum length of a package name is 39 characters. A valid package scope matches the following regular expression pattern: ```regexp -\A[a-zA-Z\d](?:[a-zA-Z\d]|-(?=[a-zA-Z\d])){0,39}\z +\A[a-zA-Z\d](?:[a-zA-Z\d]|-(?=[a-zA-Z\d])){0,38}\z ``` A package's *name* uniquely identifies a package in a scope. From 5b568fa699a50ec279e8b80365b19f01a9266d6e Mon Sep 17 00:00:00 2001 From: Mattt Date: Mon, 5 Apr 2021 04:35:22 -0700 Subject: [PATCH 02/10] Package names are compared using NFKC + CaseFolding --- proposals/0292-package-registry-service.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index e00c077502..9bd3b51568 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -163,16 +163,9 @@ A valid package name matches the following regular expression pattern: > For more information, > see [Unicode Identifier and Pattern Syntax][UAX31]. -Package scopes are case-insensitive -(for example, `mona` ≍ `MONA`). -Package names are -case-insensitive, -diacritic-insensitive -(for example, `Å` ≍ `A`), and -width-insensitive -(for example, `A` ≍ `A`). Package names are compared using -[Normalization Form Compatible Composition (NFKC)][UAX15]. +[Normalization Form Compatible Composition (NFKC)][UAX15] +with locale-independent case folding. #### New `PackageDescription` API From 4f019d361d1629bb5655e2152ba5c3d6cc6555e9 Mon Sep 17 00:00:00 2001 From: Mattt Date: Mon, 5 Apr 2021 04:35:44 -0700 Subject: [PATCH 03/10] The default, unscoped registry is denoted by [default] --- proposals/0292-package-registry-service.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 9bd3b51568..168670de8f 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -450,6 +450,8 @@ in the root directory of a package creates or updates the `.swiftpm/config/registries.json` file with a new top-level `registries` key that's associated with an object containing the specified registry URLs. +The default, unscoped registry is associated with the key `[default]`. +Any scoped registries are keyed by their case-folded name. For example, a build server that doesn't allow external network connections @@ -464,8 +466,8 @@ $ cat .swiftpm/config/registries.json ```json { "registries": { - "default": { - "url": "https://internal.example.com" + "[default]": { + "url": "https://internal.example.com" } }, "version": 1 @@ -550,8 +552,8 @@ consider the following global and local registry configuration files: ```jsonc // Global configuration (~/.swiftpm/config/registries.json) { - "registries": { - "default": { + "registries": { + "[default]": { "url": "https://global.example.com" }, "foo": { @@ -779,7 +781,7 @@ and attempt to reuse those credentials to impersonate the user. ```json { "registries": { - "default": { + "[default]": { "url": "https://:@swift.pkg.github.com//" } }, From 526fe331e4ccec1d0f07f8b0a134d5d653eda96b Mon Sep 17 00:00:00 2001 From: Mattt Date: Mon, 5 Apr 2021 04:36:03 -0700 Subject: [PATCH 04/10] Mention .netrc file as mitigation for hardcoded credentials --- proposals/0292-package-registry-service.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 168670de8f..7f01b252c3 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -791,7 +791,8 @@ and attempt to reuse those credentials to impersonate the user. ``` This kind of attack can be mitigated on an individual basis -by adding `.swiftpm/config` to a project's `.gitignore` file. +by adding `.swiftpm/config` to a project's `.gitignore` file +or storing credentials in a `.netrc` file. The risk could be mitigated for all users if Swift Package Manager included a `.gitignore` file in its new project template. From 79d1377f7e81d87cd2d8e9ea45789c5315de2872 Mon Sep 17 00:00:00 2001 From: Mattt Date: Mon, 5 Apr 2021 04:36:14 -0700 Subject: [PATCH 05/10] Remove trailing whitespace --- proposals/0292-package-registry-service.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 7f01b252c3..790efd1c40 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -235,7 +235,7 @@ that is, the `package` parameter in `.product(name:package)` method calls. ```diff targets: [ - .target(name: "MyLibrary", + .target(name: "MyLibrary", dependencies: [ .product(name: "LinkedList", - package: "LinkedList") @@ -502,7 +502,7 @@ $ cat .swiftpm/config/registries.json ```json { - "registries": { + "registries": { "example": { "url": "https://internal.example.com" } @@ -565,7 +565,7 @@ consider the following global and local registry configuration files: // Local configuration (.swiftpm/config/registries.json) { - "registries": { + "registries": { "foo": { "url": "https://local.example.com" } From 372c605773f76da530ea7c9d20639603412a456c Mon Sep 17 00:00:00 2001 From: Mattt Date: Mon, 5 Apr 2021 09:19:17 -0700 Subject: [PATCH 06/10] Fix typo name -> scope --- proposals/0292-package-registry-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 790efd1c40..24f8acfd98 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -145,7 +145,7 @@ A package scope consists of alphanumeric characters and hyphens. Hyphens may not occur at the beginning or end, nor consecutively within a scope. -The maximum length of a package name is 39 characters. +The maximum length of a package scope is 39 characters. A valid package scope matches the following regular expression pattern: ```regexp From ac89166961deb77d125c451a9db9f7ace9b7d10b Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 4 May 2021 06:06:14 -0700 Subject: [PATCH 07/10] Add link to draft registry proposal in @apple/swift-package-manager --- proposals/0292-package-registry-service.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 24f8acfd98..25d81da523 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -117,8 +117,8 @@ and downloading the source archive for a release: | `GET` | `/{scope}/{name}/{version}.zip` | Download source archive for a package release | | `GET` | `/identifiers{?url}` | Lookup package identifiers registered for a URL | -A formal specification for the package registry interface -is provided alongside this proposal. +A formal specification for the package registry interface is provided +[alongside this proposal](https://github.com/apple/swift-package-manager/blob/main/Documentation/RegistryDraft.md). In addition, an OpenAPI (v3) document and a reference implementation written in Swift From d44d47af3765923093ac8f0d132a5aa4d6bd6c56 Mon Sep 17 00:00:00 2001 From: Mattt Date: Tue, 18 May 2021 03:42:38 -0700 Subject: [PATCH 08/10] Update Information disclosure section --- proposals/0292-package-registry-service.md | 61 ++++++++++++---------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 25d81da523..52fe7495e2 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -771,42 +771,30 @@ can both provide similar non-repudiation guarantees. ### Information disclosure -A user may inadvertently reveal the existence of a private registry -or expose hardcoded credentials -by checking in their project's `.swiftpm/config` directory. +A user may inadvertently expose credentials +by checking in their project's configuration files. +An attacker could scrape public code repositories for configuration files +and attempt to reuse credentials to impersonate the user. + +The risk of leaking credentials can be mitigated by +storing them in a `.netrc` file located outside the project directory +(typically in the user's home directory). +However, +a user may run `swift package` subcommands with the `--netrc-file` option +to configure the location of their project's `.netrc` file. +To mitigate the risk of a user inadvertently +adding a local `.netrc` file to version control, +Swift Package Manager could add an entry to the `.gitignore` file template +for new projects created with `swift package init`. -An attacker could scrape public code repositories for `.swiftpm/config` files -and attempt to reuse those credentials to impersonate the user. - -```json -{ - "registries": { - "[default]": { - "url": "https://:@swift.pkg.github.com//" - } - }, - "version": 1 -} - -``` - -This kind of attack can be mitigated on an individual basis -by adding `.swiftpm/config` to a project's `.gitignore` file -or storing credentials in a `.netrc` file. -The risk could be mitigated for all users -if Swift Package Manager included a `.gitignore` file -in its new project template. Code hosting providers can also help minimize this risk by [detecting secrets][secret scanning] that are committed to public repositories. Credentials may also be unintentionally disclosed by Swift Package Manager or other tools in logging statements. -Care should be taken to redact the user info component of URLs -when displaying feedback to the user -(for example, -the URL `https://:@swift.pkg.github.com` -is logged as `https://***@swift.pkg.github.com`). +Care should be taken to redact usernames and passwords +when displaying feedback to the user. ### Denial of service @@ -816,10 +804,25 @@ that declare one or more custom registries and launch a denial-of-service attack in an attempt to reduce the availability of those resources. +```json +{ + "registries": { + "[default]": { + "url": "https://private.example.com" + } + }, + "version": 1 +} + +``` + The likelihood of this attack is generally low but could be used in a targeted way against resources known to be important or expensive to distribute. +This kind of attack can be mitigated on an individual basis +by adding `.swiftpm/config` to a project's `.gitignore` file. + ### Escalation of privilege Even authentic packages from trusted creators can contain malicious code. From ed0c44032f3a914768900fbd84a2ac9015a31ccd Mon Sep 17 00:00:00 2001 From: Mattt Date: Thu, 20 May 2021 10:03:56 -0700 Subject: [PATCH 09/10] Add section discussing authentication --- proposals/0292-package-registry-service.md | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 52fe7495e2..03b22929d8 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -443,6 +443,8 @@ SYNOPSIS OPTIONS: --global Apply settings to all projects for this user --scope Associate the registry with a given scope + --login Specify a user name for the remote machine + --password Supply a password for the remote machine ``` Running the `package-registry set` subcommand @@ -591,6 +593,57 @@ in descending order of precedence: * Any local configuration (`./.swiftpm/config/registries.json`) * Any global configuration file (`~/.swiftpm/config/registries.json`) +#### Specifying credentials for a custom registry + +Some servers may require a username and password. +The user can provide credentials when setting a custom registry +by passing the `--login` and `--password` options. + +When credentials are provided, +the corresponding object in the `registries.json` file +includes a `login` key with the passed value. +If the project's `.netrc` file has an existing entry +for a given machine and login, +it's updated with the new password; +otherwise, a new entry is added. +If no `.netrc` file exists, +a new one is created and populated with the new entry. + +```terminal +$ swift package-registry set https://internal.example.com/ \ + --login jappleseed --password alpine + +$ cat .netrc +machine internal.example.com +login jappleseed +password alpine + +$ cat .swiftpm/config/registries.json + +{ + "registries": { + "[default]": { + "url": "https://internal.example.com" + "login": "jappleseed" + } + }, + "version": 1 +} +``` + +If the user passes the `--login` and `--password` options +to the `set` subcommand along with the `--global` option, +the user-level `.netrc` file is updated instead. +When Swift Package Manager connects to a custom registry, +it first consults the project's `.netrc` file, if one exists. +If no entry is found for the custom registry, +Swift Package Manager then consults the user-level `.netrc` file, if one exists. + +If the provided credentials are missing or invalid, +Swift Package Manager commands like +`swift package resolve` and `swift package update` +fail with an error. + ### Changes to config subcommand #### Set-mirror option for package identifiers From c03e6129700b375f06a13eba9dcc4d187961c4ca Mon Sep 17 00:00:00 2001 From: Mattt Date: Thu, 20 May 2021 10:20:02 -0700 Subject: [PATCH 10/10] Use console syntax for terminal code blocks --- proposals/0292-package-registry-service.md | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 03b22929d8..0e4fff3060 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -299,7 +299,7 @@ Swift Package Manager compares the integrity checksum provided by the server against any existing checksum for that release in the `Package.resolved` file as well as the integrity checksum reported by the `compute-checksum` subcommand: -```terminal +```console $ swift package compute-checksum LinkedList-1.2.0.zip 1feec3d8d144814e99e694cd1d785928878d8d6892c4e59d12569e179252c535 ``` @@ -332,7 +332,7 @@ a package's contents may have changed at some point. Swift Package Manager will refuse to download dependencies if there's a mismatch in integrity checksums. -```terminal +```console $ swift build error: checksum of downloaded source archive of dependency 'mona.LinkedList' (c2b934fe66e55747d912f1cfd03150883c4f037370c40ca2ad4203805db79457) does not match checksum specified by the manifest (ed008d5af44c1d0ea0e3668033cae9b695235f18b1a99240b7cf0f3d9559a30d) ``` @@ -366,7 +366,7 @@ in the root directory of a package to generate a source archive for the current working tree. For example: -```terminal +```console $ tree -a -L 1 LinkedList ├── .git @@ -392,7 +392,7 @@ the name of the package with a `.zip` extension (for example, "LinkedList.zip"). You can override this behavior with the `--output` option: -```terminal +```console $ git checkout 1.2.0 $ swift package archive-source --output="LinkedList-1.2.0.zip" # Created LinkedList-1.2.0.zip @@ -403,7 +403,7 @@ The `archive-source` subcommand has the equivalent behavior of Therefore, the following command produces equivalent output to the previous example: -```terminal +```console $ git archive --format zip --output LinkedList-1.2.0.zip 1.2.0 ``` @@ -460,7 +460,7 @@ a build server that doesn't allow external network connections may configure a registry URL to resolve dependencies using an internal registry service. -```terminal +```console $ swift package-registry set https://internal.example.com/ $ cat .swiftpm/config/registries.json ``` @@ -482,7 +482,7 @@ Swift Package Manager commands like `swift package resolve` and `swift package update` fail with an error. -```terminal +```console $ swift package resolve error: cannot resolve dependency 'mona.LinkedList' without a configured registry ``` @@ -497,7 +497,7 @@ a user might resolve all packages with the package scope `example` (such as `example.PriorityQueue`) to a private registry. -```terminal +```console $ swift package-registry set https://internal.example.com/ --scope example $ cat .swiftpm/config/registries.json ``` @@ -609,7 +609,7 @@ otherwise, a new entry is added. If no `.netrc` file exists, a new one is created and populated with the new entry. -```terminal +```console $ swift package-registry set https://internal.example.com/ \ --login jappleseed --password alpine @@ -651,7 +651,7 @@ fail with an error. A user can currently specify an alternate location for a package by setting a [dependency mirror][SE-0219] for that package's URL. -```terminal +```console $ swift package config set-mirror \ --original-url https:///github.com/mona/linkedlist \ --mirror-url https:///github.com/octocorp/swiftlinkedlist @@ -765,7 +765,7 @@ Although the impact of such an attack is potentially high, the risk is largely mitigated by the use of cryptographic checksums to verify the integrity of downloaded source archives. -```terminal +```console $ echo "$(swift package compute-checksum LinkedList-1.2.0.zip) *LinkedList-1.2.0.zip" | \ shasum -a 256 -c - LinkedList-1.2.0.zip: OK @@ -802,7 +802,7 @@ If the history of a project is available and the commit used to generate the source archive is signed with [GPG], the cryptographic signature may be used to verify the authenticity. -```terminal +```console $ git rev-parse HEAD b7c37c81f164e5dce0f64e3d75c79a48fb1fe00b3 @@ -1099,7 +1099,7 @@ let package = Package( could be extended to add dependencies using scoped identifiers in addition to URLs. -```terminal +```console $ swift package add-dependency mona.LinkedList # Installed LinkedList 1.2.0 ``` @@ -1114,7 +1114,7 @@ Swift Package Manager could add tooling to help package maintainers adopt registry-supported identifiers in their projects. -```terminal +```console $ swift package-registry migrate ``` @@ -1148,7 +1148,7 @@ Swift Package Manager could communicate this information to users when installing or updating dependencies or as part of a new `swift package audit` subcommand. -```terminal +```console $ swift package audit ┌───────────────┬────────────────────────────────────────────────┐ │ High │ Regular Expression Denial of Service │ @@ -1172,7 +1172,7 @@ The package registry API could be extended to add a search endpoint to allow users to search for packages by name, keywords, or other criteria. This endpoint could be used by clients like Swift Package Manager. -```terminal +```console $ swift package search LinkedList LinkedList (github.com/mona/LinkedList) - One thing links to another.