Skip to content

Commit 02292fe

Browse files
authored
Incorporate feedback from 2nd review of SE-0292 (#1319)
* Fix regular expression for package scopes * Package names are compared using NFKC + CaseFolding * The default, unscoped registry is denoted by [default] * Mention .netrc file as mitigation for hardcoded credentials * Add link to draft registry proposal in @apple/swift-package-manager * Update Information disclosure section * Add section discussing authentication
1 parent 03b596e commit 02292fe

File tree

1 file changed

+115
-63
lines changed

1 file changed

+115
-63
lines changed

proposals/0292-package-registry-service.md

Lines changed: 115 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ and downloading the source archive for a release:
120120
| `GET` | `/{scope}/{name}/{version}.zip` | Download source archive for a package release |
121121
| `GET` | `/identifiers{?url}` | Lookup package identifiers registered for a URL |
122122

123-
A formal specification for the package registry interface
124-
is provided alongside this proposal.
123+
A formal specification for the package registry interface is provided
124+
[alongside this proposal](https://github.com/apple/swift-package-manager/blob/main/Documentation/RegistryDraft.md).
125125
In addition,
126126
an OpenAPI (v3) document
127127
and a reference implementation written in Swift
@@ -148,11 +148,11 @@ A package scope consists of
148148
alphanumeric characters and hyphens.
149149
Hyphens may not occur at the beginning or end,
150150
nor consecutively within a scope.
151-
The maximum length of a package name is 39 characters.
151+
The maximum length of a package scope is 39 characters.
152152
A valid package scope matches the following regular expression pattern:
153153

154154
```regexp
155-
\A[a-zA-Z\d](?:[a-zA-Z\d]|-(?=[a-zA-Z\d])){0,39}\z
155+
\A[a-zA-Z\d](?:[a-zA-Z\d]|-(?=[a-zA-Z\d])){0,38}\z
156156
```
157157

158158
A package's *name* uniquely identifies a package in a scope.
@@ -166,16 +166,9 @@ A valid package name matches the following regular expression pattern:
166166
> For more information,
167167
> see [Unicode Identifier and Pattern Syntax][UAX31].
168168
169-
Package scopes are case-insensitive
170-
(for example, `mona``MONA`).
171-
Package names are
172-
case-insensitive,
173-
diacritic-insensitive
174-
(for example, `Å``A`), and
175-
width-insensitive
176-
(for example, ```A`).
177169
Package names are compared using
178-
[Normalization Form Compatible Composition (NFKC)][UAX15].
170+
[Normalization Form Compatible Composition (NFKC)][UAX15]
171+
with locale-independent case folding.
179172

180173
#### New `PackageDescription` API
181174

@@ -245,7 +238,7 @@ that is, the `package` parameter in `.product(name:package)` method calls.
245238

246239
```diff
247240
targets: [
248-
.target(name: "MyLibrary",
241+
.target(name: "MyLibrary",
249242
dependencies: [
250243
.product(name: "LinkedList",
251244
- package: "LinkedList")
@@ -309,7 +302,7 @@ Swift Package Manager compares the integrity checksum provided by the server
309302
against any existing checksum for that release in the `Package.resolved` file
310303
as well as the integrity checksum reported by the `compute-checksum` subcommand:
311304

312-
```terminal
305+
```console
313306
$ swift package compute-checksum LinkedList-1.2.0.zip
314307
1feec3d8d144814e99e694cd1d785928878d8d6892c4e59d12569e179252c535
315308
```
@@ -342,7 +335,7 @@ a package's contents may have changed at some point.
342335
Swift Package Manager will refuse to download dependencies
343336
if there's a mismatch in integrity checksums.
344337

345-
```terminal
338+
```console
346339
$ swift build
347340
error: checksum of downloaded source archive of dependency 'mona.LinkedList' (c2b934fe66e55747d912f1cfd03150883c4f037370c40ca2ad4203805db79457) does not match checksum specified by the manifest (ed008d5af44c1d0ea0e3668033cae9b695235f18b1a99240b7cf0f3d9559a30d)
348341
```
@@ -376,7 +369,7 @@ in the root directory of a package
376369
to generate a source archive for the current working tree.
377370
For example:
378371

379-
```terminal
372+
```console
380373
$ tree -a -L 1
381374
LinkedList
382375
├── .git
@@ -402,7 +395,7 @@ the name of the package with a `.zip` extension
402395
(for example, "LinkedList.zip").
403396
You can override this behavior with the `--output` option:
404397

405-
```terminal
398+
```console
406399
$ git checkout 1.2.0
407400
$ swift package archive-source --output="LinkedList-1.2.0.zip"
408401
# Created LinkedList-1.2.0.zip
@@ -413,7 +406,7 @@ The `archive-source` subcommand has the equivalent behavior of
413406
Therefore, the following command produces
414407
equivalent output to the previous example:
415408

416-
```terminal
409+
```console
417410
$ git archive --format zip --output LinkedList-1.2.0.zip 1.2.0
418411
```
419412

@@ -453,29 +446,33 @@ SYNOPSIS
453446
OPTIONS:
454447
--global Apply settings to all projects for this user
455448
--scope Associate the registry with a given scope
449+
--login Specify a user name for the remote machine
450+
--password Supply a password for the remote machine
456451
```
457452

458453
Running the `package-registry set` subcommand
459454
in the root directory of a package
460455
creates or updates the `.swiftpm/config/registries.json` file
461456
with a new top-level `registries` key
462457
that's associated with an object containing the specified registry URLs.
458+
The default, unscoped registry is associated with the key `[default]`.
459+
Any scoped registries are keyed by their case-folded name.
463460

464461
For example,
465462
a build server that doesn't allow external network connections
466463
may configure a registry URL to resolve dependencies
467464
using an internal registry service.
468465

469-
```terminal
466+
```console
470467
$ swift package-registry set https://internal.example.com/
471468
$ cat .swiftpm/config/registries.json
472469
```
473470

474471
```json
475472
{
476473
"registries": {
477-
"default": {
478-
"url": "https://internal.example.com"
474+
"[default]": {
475+
"url": "https://internal.example.com"
479476
}
480477
},
481478
"version": 1
@@ -488,7 +485,7 @@ Swift Package Manager commands like
488485
`swift package resolve` and `swift package update`
489486
fail with an error.
490487

491-
```terminal
488+
```console
492489
$ swift package resolve
493490
error: cannot resolve dependency 'mona.LinkedList' without a configured registry
494491
```
@@ -503,14 +500,14 @@ a user might resolve all packages with the package scope `example`
503500
(such as `example.PriorityQueue`)
504501
to a private registry.
505502

506-
```terminal
503+
```console
507504
$ swift package-registry set https://internal.example.com/ --scope example
508505
$ cat .swiftpm/config/registries.json
509506
```
510507

511508
```json
512509
{
513-
"registries": {
510+
"registries": {
514511
"example": {
515512
"url": "https://internal.example.com"
516513
}
@@ -560,8 +557,8 @@ consider the following global and local registry configuration files:
560557
```jsonc
561558
// Global configuration (~/.swiftpm/config/registries.json)
562559
{
563-
"registries": {
564-
"default": {
560+
"registries": {
561+
"[default]": {
565562
"url": "https://global.example.com"
566563
},
567564
"foo": {
@@ -573,7 +570,7 @@ consider the following global and local registry configuration files:
573570

574571
// Local configuration (.swiftpm/config/registries.json)
575572
{
576-
"registries": {
573+
"registries": {
577574
"foo": {
578575
"url": "https://local.example.com"
579576
}
@@ -599,14 +596,65 @@ in descending order of precedence:
599596
* Any local configuration (`./.swiftpm/config/registries.json`)
600597
* Any global configuration file (`~/.swiftpm/config/registries.json`)
601598

599+
#### Specifying credentials for a custom registry
600+
601+
Some servers may require a username and password.
602+
The user can provide credentials when setting a custom registry
603+
by passing the `--login` and `--password` options.
604+
605+
When credentials are provided,
606+
the corresponding object in the `registries.json` file
607+
includes a `login` key with the passed value.
608+
If the project's `.netrc` file has an existing entry
609+
for a given machine and login,
610+
it's updated with the new password;
611+
otherwise, a new entry is added.
612+
If no `.netrc` file exists,
613+
a new one is created and populated with the new entry.
614+
615+
```console
616+
$ swift package-registry set https://internal.example.com/ \
617+
--login jappleseed --password alpine
618+
619+
$ cat .netrc
620+
machine internal.example.com
621+
login jappleseed
622+
password alpine
623+
624+
$ cat .swiftpm/config/registries.json
625+
626+
{
627+
"registries": {
628+
"[default]": {
629+
"url": "https://internal.example.com"
630+
"login": "jappleseed"
631+
}
632+
},
633+
"version": 1
634+
}
635+
```
636+
637+
If the user passes the `--login` and `--password` options
638+
to the `set` subcommand along with the `--global` option,
639+
the user-level `.netrc` file is updated instead.
640+
When Swift Package Manager connects to a custom registry,
641+
it first consults the project's `.netrc` file, if one exists.
642+
If no entry is found for the custom registry,
643+
Swift Package Manager then consults the user-level `.netrc` file, if one exists.
644+
645+
If the provided credentials are missing or invalid,
646+
Swift Package Manager commands like
647+
`swift package resolve` and `swift package update`
648+
fail with an error.
649+
602650
### Changes to config subcommand
603651

604652
#### Set-mirror option for package identifiers
605653

606654
A user can currently specify an alternate location for a package
607655
by setting a [dependency mirror][SE-0219] for that package's URL.
608656

609-
```terminal
657+
```console
610658
$ swift package config set-mirror \
611659
--original-url https:///github.com/mona/linkedlist \
612660
--mirror-url https:///github.com/octocorp/swiftlinkedlist
@@ -720,7 +768,7 @@ Although the impact of such an attack is potentially high,
720768
the risk is largely mitigated by the use of cryptographic checksums
721769
to verify the integrity of downloaded source archives.
722770

723-
```terminal
771+
```console
724772
$ echo "$(swift package compute-checksum LinkedList-1.2.0.zip) *LinkedList-1.2.0.zip" | \
725773
shasum -a 256 -c -
726774
LinkedList-1.2.0.zip: OK
@@ -757,7 +805,7 @@ If the history of a project is available
757805
and the commit used to generate the source archive is signed with [GPG],
758806
the cryptographic signature may be used to verify the authenticity.
759807

760-
```terminal
808+
```console
761809
$ git rev-parse HEAD
762810
b7c37c81f164e5dce0f64e3d75c79a48fb1fe00b3
763811

@@ -779,41 +827,30 @@ can both provide similar non-repudiation guarantees.
779827

780828
### Information disclosure
781829

782-
A user may inadvertently reveal the existence of a private registry
783-
or expose hardcoded credentials
784-
by checking in their project's `.swiftpm/config` directory.
785-
786-
An attacker could scrape public code repositories for `.swiftpm/config` files
787-
and attempt to reuse those credentials to impersonate the user.
830+
A user may inadvertently expose credentials
831+
by checking in their project's configuration files.
832+
An attacker could scrape public code repositories for configuration files
833+
and attempt to reuse credentials to impersonate the user.
834+
835+
The risk of leaking credentials can be mitigated by
836+
storing them in a `.netrc` file located outside the project directory
837+
(typically in the user's home directory).
838+
However,
839+
a user may run `swift package` subcommands with the `--netrc-file` option
840+
to configure the location of their project's `.netrc` file.
841+
To mitigate the risk of a user inadvertently
842+
adding a local `.netrc` file to version control,
843+
Swift Package Manager could add an entry to the `.gitignore` file template
844+
for new projects created with `swift package init`.
788845

789-
```json
790-
{
791-
"registries": {
792-
"default": {
793-
"url": "https://<USERNAME>:<TOKEN>@swift.pkg.github.com/<OWNER>/"
794-
}
795-
},
796-
"version": 1
797-
}
798-
799-
```
800-
801-
This kind of attack can be mitigated on an individual basis
802-
by adding `.swiftpm/config` to a project's `.gitignore` file.
803-
The risk could be mitigated for all users
804-
if Swift Package Manager included a `.gitignore` file
805-
in its new project template.
806846
Code hosting providers can also help minimize this risk
807847
by [detecting secrets][secret scanning]
808848
that are committed to public repositories.
809849

810850
Credentials may also be unintentionally disclosed
811851
by Swift Package Manager or other tools in logging statements.
812-
Care should be taken to redact the user info component of URLs
813-
when displaying feedback to the user
814-
(for example,
815-
the URL `https://<USERNAME>:<TOKEN>@swift.pkg.github.com`
816-
is logged as `https://***@swift.pkg.github.com`).
852+
Care should be taken to redact usernames and passwords
853+
when displaying feedback to the user.
817854

818855
### Denial of service
819856

@@ -823,10 +860,25 @@ that declare one or more custom registries
823860
and launch a denial-of-service attack
824861
in an attempt to reduce the availability of those resources.
825862

863+
```json
864+
{
865+
"registries": {
866+
"[default]": {
867+
"url": "https://private.example.com"
868+
}
869+
},
870+
"version": 1
871+
}
872+
873+
```
874+
826875
The likelihood of this attack is generally low
827876
but could be used in a targeted way
828877
against resources known to be important or expensive to distribute.
829878

879+
This kind of attack can be mitigated on an individual basis
880+
by adding `.swiftpm/config` to a project's `.gitignore` file.
881+
830882
### Escalation of privilege
831883

832884
Even authentic packages from trusted creators can contain malicious code.
@@ -1050,7 +1102,7 @@ let package = Package(
10501102
could be extended to add dependencies using scoped identifiers
10511103
in addition to URLs.
10521104

1053-
```terminal
1105+
```console
10541106
$ swift package add-dependency mona.LinkedList
10551107
# Installed LinkedList 1.2.0
10561108
```
@@ -1065,7 +1117,7 @@ Swift Package Manager could add tooling
10651117
to help package maintainers adopt registry-supported identifiers
10661118
in their projects.
10671119

1068-
```terminal
1120+
```console
10691121
$ swift package-registry migrate
10701122
```
10711123

@@ -1099,7 +1151,7 @@ Swift Package Manager could communicate this information to users
10991151
when installing or updating dependencies
11001152
or as part of a new `swift package audit` subcommand.
11011153

1102-
```terminal
1154+
```console
11031155
$ swift package audit
11041156
┌───────────────┬────────────────────────────────────────────────┐
11051157
│ High │ Regular Expression Denial of Service │
@@ -1123,7 +1175,7 @@ The package registry API could be extended to add a search endpoint
11231175
to allow users to search for packages by name, keywords, or other criteria.
11241176
This endpoint could be used by clients like Swift Package Manager.
11251177

1126-
```terminal
1178+
```console
11271179
$ swift package search LinkedList
11281180
LinkedList (github.com/mona/LinkedList) - One thing links to another.
11291181

0 commit comments

Comments
 (0)