Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement to credentials handling #1310

Merged
merged 1 commit into from
Aug 24, 2024
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
2 changes: 1 addition & 1 deletion extension/task/IDependabotConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export interface IDependabotRegistry {
* It should not have the scheme.
*/
'registry'?: string | null | undefined;
/** The hostname for 'terraform_registry' types */
/** The hostname for `terraform_registry` and `composer_repository` types */
'host'?: string | null | undefined;

/** The username to access the registry */
Expand Down
42 changes: 30 additions & 12 deletions extension/task/utils/parseConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getVariable } from 'azure-pipelines-task-lib/task';
import * as fs from 'fs';
import { load } from 'js-yaml';
import * as path from 'path';
import { URL } from 'url';
import { IDependabotConfig, IDependabotRegistry, IDependabotUpdate } from '../IDependabotConfig';
import { convertPlaceholder } from './convertPlaceholder';
import { ISharedVariables } from './getSharedVariables';
Expand Down Expand Up @@ -266,19 +267,36 @@ function parseRegistries(config: any): Record<string, IDependabotRegistry> {
throw new Error(`The value 'url' in dependency registry config '${registryConfigKey}' is missing`);
}
if (url) {
// Some credentials do not use the 'url' property in the Ruby updater.
// npm_registry and docker_registry use 'registry' which should be stripped off the scheme.
// terraform_registry uses 'host' which is the hostname from the given URL.

if (type === 'docker_registry' || type === 'npm_registry') {
parsed.registry = url.replace('https://', '').replace('http://', '');
} else if (type === 'terraform_registry') {
parsed.host = new URL(url).hostname;
} else if (type === 'python_index') {
parsed['index-url'] = url;
} else {
parsed.url = url;
/*
* Some credentials do not use the 'url' property in the Ruby updater.
* The 'host' and 'registry' properties are derived from the given URL.
* The 'registry' property is derived from the 'url' by stripping off the scheme.
* The 'host' property is derived from the hostname of the 'url'.
*
* 'npm_registry' and 'docker_registry' use 'registry' only.
* 'terraform_registry' uses 'host' only.
* 'composer_repository' uses both 'url' and 'host'.
* 'python_index' uses 'index-url' instead of 'url'.
*/

if (URL.canParse(url)) {
const parsedUrl = new URL(url);

const addRegistry = type === 'docker_registry' || type === 'npm_registry';
if (addRegistry) parsed.registry = url.replace('https://', '').replace('http://', '');

const addHost = type === 'terraform_registry' || type === 'composer_repository';
if (addHost) parsed.host = parsedUrl.hostname;
}

if (type === 'python_index') parsed['index-url'] = url;

const skipUrl =
type === 'docker_registry' ||
type === 'npm_registry' ||
type === 'terraform_registry' ||
type === 'python_index';
if (!skipUrl) parsed.url = url;
}
});
return registries;
Expand Down
2 changes: 1 addition & 1 deletion extension/tests/utils/parseConfigFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('Parse registries', () => {
expect(registry.url).toBe('https://repo.packagist.com/example-company/');
expect(registry['index-url']).toBe(undefined);
expect(registry.registry).toBe(undefined);
expect(registry.host).toBe(undefined);
expect(registry.host).toBe('repo.packagist.com');
expect(registry.key).toBe(undefined);
expect(registry.token).toBe(undefined);
expect(registry.organization).toBe(undefined);
Expand Down
16 changes: 14 additions & 2 deletions server/Tingle.Dependabot.Tests/Workflow/UpdateRunnerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public void MakeExtraCredentials_Works()
Assert.Equal("composer_repository", Assert.Contains("type", credential));
Assert.Equal("https://repo.packagist.com/example-company/", Assert.Contains("url", credential));
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("index-url", credential);
Assert.Equal("repo.packagist.com", Assert.Contains("host", credential));
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Assert.DoesNotContain("organization", credential);
Expand All @@ -117,6 +118,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("docker_registry", Assert.Contains("type", credential));
Assert.DoesNotContain("url", credential);
Assert.Equal("registry.hub.docker.com", Assert.Contains("registry", credential));
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Expand All @@ -133,6 +135,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("git", Assert.Contains("type", credential));
Assert.Equal("https://github.com", Assert.Contains("url", credential));
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Expand All @@ -149,6 +152,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("hex_organization", Assert.Contains("type", credential));
Assert.DoesNotContain("url", credential);
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.Equal("key_1234567890", Assert.Contains("key", credential));
Assert.DoesNotContain("token", credential);
Expand All @@ -165,6 +169,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("hex_repository", Assert.Contains("type", credential));
Assert.Equal("https://private-repo.example.com", Assert.Contains("url", credential));
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Expand All @@ -181,6 +186,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("maven_repository", Assert.Contains("type", credential));
Assert.Equal("https://artifactory.example.com", Assert.Contains("url", credential));
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Expand All @@ -197,6 +203,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("npm_registry", Assert.Contains("type", credential));
Assert.DoesNotContain("url", credential);
Assert.Equal("npm.pkg.github.com", Assert.Contains("registry", credential));
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.Equal("tkn_1234567890", Assert.Contains("token", credential));
Expand All @@ -213,6 +220,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("nuget_feed", Assert.Contains("type", credential));
Assert.Equal("https://pkgs.dev.azure.com/contoso/_packaging/My_Feed/nuget/v3/index.json", Assert.Contains("url", credential));
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Expand All @@ -227,15 +235,17 @@ public void MakeExtraCredentials_Works()
// python-index
credential = credentials[8];
Assert.Equal("python_index", Assert.Contains("type", credential));
Assert.Equal("https://pkgs.dev.azure.com/octocat/_packaging/my-feed/pypi/example", Assert.Contains("url", credential));
Assert.DoesNotContain("url", credential);
Assert.DoesNotContain("registry", credential);
Assert.Equal("https://pkgs.dev.azure.com/octocat/_packaging/my-feed/pypi/example", Assert.Contains("index-url", credential));
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.DoesNotContain("token", credential);
Assert.DoesNotContain("organization", credential);
Assert.DoesNotContain("repo", credential);
Assert.DoesNotContain("auth-key", credential);
Assert.DoesNotContain("public-key-fingerprint", credential);
Assert.Equal("https://pkgs.dev.azure.com/octocat/_packaging/my-feed/pypi/example", Assert.Contains("index-url", credential));
Assert.Equal("[email protected]", Assert.Contains("username", credential));
Assert.Equal("pwd_1234567890", Assert.Contains("password", credential));
Assert.Equal("true", Assert.Contains("replaces-base", credential));
Expand All @@ -245,6 +255,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("rubygems_server", Assert.Contains("type", credential));
Assert.Equal("https://rubygems.pkg.github.com/octocat/github_api", Assert.Contains("url", credential));
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.DoesNotContain("host", credential);
Assert.DoesNotContain("key", credential);
Assert.Equal("tkn_1234567890", Assert.Contains("token", credential));
Expand All @@ -261,6 +272,7 @@ public void MakeExtraCredentials_Works()
Assert.Equal("terraform_registry", Assert.Contains("type", credential));
Assert.DoesNotContain("url", credential);
Assert.DoesNotContain("registry", credential);
Assert.DoesNotContain("index-url", credential);
Assert.Equal("terraform.example.com", Assert.Contains("host", credential));
Assert.DoesNotContain("key", credential);
Assert.Equal("tkn_1234567890", Assert.Contains("token", credential));
Expand Down
55 changes: 31 additions & 24 deletions server/Tingle.Dependabot/Workflow/UpdateRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -384,18 +384,17 @@ internal static IList<Dictionary<string, string>> MakeCredentialsMetadata(IList<
return credentials.Select(cred =>
{
var values = new Dictionary<string, string> { ["type"] = cred["type"], };
cred.TryGetValue("host", out var host);

// pull host from registry if available
if (string.IsNullOrWhiteSpace(host))
// if no host, pull host from url, index-url, or registry if available
if (!cred.TryGetValue("host", out var host) || string.IsNullOrWhiteSpace(host))
{
host = cred.TryGetValue("registry", out var registry) && Uri.TryCreate($"https://{registry}", UriKind.Absolute, out var u) ? u.Host : host;
}
if (cred.TryGetValue("url", out var url) || cred.TryGetValue("index-url", out url)) { }
else if (cred.TryGetValue("registry", out var registry)) url = $"https://{registry}";

// pull host from registry if url
if (string.IsNullOrWhiteSpace(host))
{
host = cred.TryGetValue("url", out var url) && Uri.TryCreate(url, UriKind.Absolute, out var u) ? u.Host : host;
if (url is not null && Uri.TryCreate(url, UriKind.Absolute, out var u))
{
host = u.Host;
}
}

values.AddIfNotDefault("host", host);
Expand Down Expand Up @@ -427,23 +426,31 @@ internal static IList<Dictionary<string, string>> MakeExtraCredentials(ICollecti
values.AddIfNotDefault("token", ConvertPlaceholder(v.Token, secrets));
values.AddIfNotDefault("replaces-base", v.ReplacesBase is true ? "true" : null);

// Some credentials do not use the 'url' property in the Ruby updater.
// npm_registry and docker_registry use 'registry' which should be stripped off the scheme.
// terraform_registry uses 'host' which is the hostname from the given URL.

if (type == "docker_registry" || type == "npm_registry")
{
values.Add("registry", v.Url!.Replace("https://", "").Replace("http://", ""));
}
else if (type == "terraform_registry")
/*
* Some credentials do not use the 'url' property in the Ruby updater.
* The 'host' and 'registry' properties are derived from the given URL.
* The 'registry' property is derived from the 'url' by stripping off the scheme.
* The 'host' property is derived from the hostname of the 'url'.
*
* 'npm_registry' and 'docker_registry' use 'registry' only.
* 'terraform_registry' uses 'host' only.
* 'composer_repository' uses both 'url' and 'host'.
* 'python_index' uses 'index-url' instead of 'url'.
*/

if (Uri.TryCreate(v.Url, UriKind.Absolute, out var url))
{
values.Add("host", new Uri(v.Url!).Host);
}
else
{
values.AddIfNotDefault("url", v.Url!);
var addRegistry = type is "docker_registry" or "npm_registry";
if (addRegistry) values.Add("registry", $"{url.Host}{url.PathAndQuery}".TrimEnd('/'));

var addHost = type is "terraform_registry" or "composer_repository";
if (addHost) values.Add("host", url.Host);
}
var useRegistryProperty = type.Contains("npm") || type.Contains("docker");

if (type is "python_index") values.AddIfNotDefault("index-url", v.Url);

var skipUrl = type is "docker_registry" or "npm_registry" or "terraform_registry" or "python_index";
if (!skipUrl) values.AddIfNotDefault("url", v.Url);

return values;
}).ToList();
Expand Down