Skip to content

Make Swift Detector experimental - #1866

Open
Ryan Brandenburg (ryanbrandenburg) wants to merge 4 commits into
mainfrom
users/rybrande/SwiftExperiment
Open

Ryan Brandenburg (ryanbrandenburg) wants to merge 4 commits into
mainfrom
users/rybrande/SwiftExperiment

Conversation

@ryanbrandenburg

Copy link
Copy Markdown
Contributor

This detector exists but isn't getting a lot of use since it's off by default. Let's make it an experiment so it gets a bit of exercise.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The only noted issue is a non-blocking documentation nit.

Review tier: Lite
Findings: None

What changed in this PR

Makes the Swift Package Manager detector experimental.

Changes:

  • Implements experimental detector behavior.
  • Adds lifecycle coverage.
  • Updates detector documentation.
File Summary
test/​Microsoft.ComponentDetection.Detectors.Tests/​ComponentDetectorTests.cs Verifies Swift’s experimental status.
src/​Microsoft.ComponentDetection.Detectors/​swiftpm/​SwiftResolvedComponentDetector.cs Changes the detector lifecycle classification.
docs/​detectors/​README.md Documents the detector status.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI review requested due to automatic review settings September 15, 2026 23:59
@github-actions

Copy link
Copy Markdown

👋 Hi! It looks like you modified some files in the Detectors folder.
You may need to bump the detector versions if any of the following scenarios apply:

  • The detector detects more or fewer components than before
  • The detector generates different parent/child graph relationships than before
  • The detector generates different devDependencies values than before

If none of the above scenarios apply, feel free to ignore this comment 🙂

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Multiple public constructors can make System.Text.Json deserialization of SwiftComponent fail; add serializer compatibility and a deserialization test.

Get a fresh assessment by requesting another Copilot review.

Review tier: Lite
Findings: 1 High severity

Open (1)

Comment thread src/Microsoft.ComponentDetection.Contracts/TypedComponent/SwiftComponent.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Constructor mismatches cause compilation failures and swap the Kind and CommitHash values.

Get a fresh assessment by requesting another Copilot review.

Review tier: Lite
Findings: 3 High severity · 1 Medium severity

Open (4)
Resolved since last review (1)

public SwiftComponent(string name, string version, string packageUrl, string hash)
/// <param name="hash">The Git commit hash of the component.</param>
/// <param name="kind">The Swift package kind.</param>
public SwiftComponent(string name, string version, string packageUrl, string hash, string kind)
Comment thread test/Microsoft.ComponentDetection.Detectors.Tests/SwiftComponentTests.cs Outdated
Comment thread test/Microsoft.ComponentDetection.Detectors.Tests/SwiftComponentTests.cs Outdated
hash: package.State.Revision);
var newDetectedSwiftComponent = new DetectedComponent(component: detectedSwiftComponent);
singleFileComponentRecorder.RegisterUsage(newDetectedSwiftComponent);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this being removed? If a swift package manager is using a git url one can get additional vulnerability coverage, see https://osv.dev/list?ecosystem=GIT. Not all swift components come from git tho, so adding the relevant condition is necessary to avoid noise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First some context

A standard "pins" entry in a Package.resolved file looks like the following:

{
  "identity" : "aexml",
  "kind" : "remoteSourceControl",
  "location" : "https://github.com/tadija/AEXML.git",
  "state" : {
    "revision" : "db806756c989760b35108146381535aec231092b",
    "version" : "4.7.0"
  }
},

There exist other kinds such as "registry", "local", and sourceControl but they are explicitly not supported by the current detector and seem less common.

And here's some example entries from before my change, both from the same ScanManifest output file:
The SwiftComponent

{
  "locationsFoundAt": [
    "/Cuckoo/Generator/Generator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"
  ],
  "component": {
    "type": "Swift",
    "name": "aexml",
    "version": "4.7.0",
    "packageUrl": {
      "Scheme": "pkg",
      "Type": "swift",
      "Namespace": "github.com/tadija",
      "Name": "aexml",
      "Version": "4.7.0",
      "Qualifiers": {
        "repository_url": "https://github.com/tadija/AEXML.git"
      },
      "Subpath": null
    },
    "id": "aexml 4.7.0 - Swift"
  },
  "detectorId": "Swift",
  "isDevelopmentDependency": null,
  "dependencyScope": null,
  "topLevelReferrers": [],
  "ancestralReferrers": [],
  "containerDetailIds": [],
  "containerLayerIds": {},
  "targetFrameworks": []
},

The GitComponent

{
  "locationsFoundAt": [
    "/Cuckoo/Generator/Generator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"
  ],
  "component": {
    "type": "Git",
    "repositoryUrl": "https://github.com/tadija/AEXML.git",
    "commitHash": "db806756c989760b35108146381535aec231092b",
    "tag": "4.7.0",
    "packageUrl": {
      "Scheme": "pkg",
      "Type": "github",
      "Namespace": "tadija",
      "Name": "aexml",
      "Version": "db806756c989760b35108146381535aec231092b",
      "Qualifiers": null,
      "Subpath": null
    },
    "id": "https://github.com/tadija/AEXML.git : db806756c989760b35108146381535aec231092b - Git"
  },
  "detectorId": "Swift",
  "isDevelopmentDependency": null,
  "dependencyScope": null,
  "topLevelReferrers": [],
  "ancestralReferrers": [],
  "containerDetailIds": [],
  "containerLayerIds": {},
  "targetFrameworks": []
},

The commit hash was the only bit of information present in the GitComponent but not in the SwiftComponent, so I thought we could prevent some confusion/duplication by only reporting a component once. If GitComponent's get some kind of special treatment then I think there's a case for making all "kind" : "remoteSourceControl", entries (the only kind currently supported but that's a different question) GitComponents and not emitting the SwiftComponent at all (until/unless we support "kind": "registry" or others).

[JsonPropertyName("version")]
public string Version { get; }

[JsonPropertyName("kind")]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we truly need to commit hash and Kind? Mind adding context on why these was added, possibly Kind makes sense, but commit hash might explode the data a bit, or we can end up with duplicate packages if one has commit and the other one does not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it is possible to have a Package.resolved remoteSourceControl entry without a "revision" (hash). Here's some relevant code from SwiftPM. Unfortunately they don't have a formal specification for Package.resolved files but it would make sense that for your Git-based package manager the revision would always be required (and controlling) because the tag/version can be changed at a later date (and it would even be quite common for scenerios like "latest"). I'd go so far as to say that a Package.resolved remoteSourceControl entry without state.revision is malformed.

I think there's a stronger case for removing "kind" since right now we explicitly only support "remoteSourceControl" values out of "kind", but I would still vote to include it to make future support for "kind": "registry" more straightforward, and so that we can add statistics for the same to see if adding that support is worthwhile.

I'm less worried about exploding the data here since as long as we only keep 1 of the SwiftComponent and GitComponent we're starting out having halved our output from this detector.

},
subpath: null);

protected override string ComputeBaseId() => $"{this.Name} {this.Version} - {this.Type}";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this component ID, is what makes the component unique across a scan. If we are adding new properties we need to review if they indeed make a component unique and include here, or update existing entry to include the additional fields, etc. We should be consistent tho, and have a reason why to capture that metadata.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch. If we land on reporting "remoteSourceControl" as a SwiftComponent I think we should update this to include the CommitHash, and potentially remove the version until we have support for kind=registry. If we report remoteSourceControl as GitComponent then I don't think this file will get used at all until we add registry support.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Address the SwiftComponent serialization and public-constructor compatibility issues before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 High severity

Open (2)
Resolved since last review (3)

Comment on lines +34 to +36
public SwiftComponent()
{
/* Reserved for deserialization */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants