Skip to content
Open
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
11 changes: 11 additions & 0 deletions .changeset/1887.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@asyncapi/cli': patch
---

fix: resolve SonarCloud issues in config and validation services

- 656f398: fix: resolve SonarCloud issues in config and validation services

Fixes #1881


6 changes: 3 additions & 3 deletions src/apps/cli/commands/config/auth/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { blueBright } from 'picocolors';
import { ConfigService, AuthEntry } from '@/domains/services/config.service';

export default class AuthAdd extends Command {
static description =
static readonly description =
'Add an authentication config for resolving $ref files requiring HTTP Authorization.';

static args = {
static readonly args = {
pattern: Args.string({
required: true,
description:
Expand All @@ -20,7 +20,7 @@ export default class AuthAdd extends Command {
}),
};

static flags = {
static readonly flags = {
'auth-type': Flags.string({
char: 'a',
description: 'Authentication type (default is "Bearer")',
Expand Down
16 changes: 7 additions & 9 deletions src/domains/services/config.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import os from 'os';
import { promises as fs } from 'fs';
import path from 'node:path';
import os from 'node:os';
import { promises as fs } from 'node:fs';

const CONFIG_DIR = path.join(os.homedir(), '.asyncapi');
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
Expand Down Expand Up @@ -51,9 +51,7 @@ export class ConfigService {
*/
static async addAuthEntry(entry: AuthEntry): Promise<void> {
const config = await this.loadConfig();
if (!config.auth) {
config.auth = [];
}
config.auth ??= [];
config.auth.push(entry);
await this.saveConfig(config);
}
Expand Down Expand Up @@ -96,11 +94,11 @@ export class ConfigService {
* @param pattern - wildcard pattern
*/
private static wildcardToRegex(pattern: string): RegExp {
const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&');
const escaped = pattern.replaceAll(/[-/\\^$+?.()|[\]{}]/g, String.raw`\$&`);

const regexStr = escaped
.replace(/\*\*/g, '.*')
.replace(/\*/g, '[^/]*');
.replaceAll('**', '.*')
.replaceAll('*', '[^/]*');

// eslint-disable-next-line security/detect-non-literal-regexp
return new RegExp(`^${regexStr}`);
Expand Down
106 changes: 69 additions & 37 deletions src/domains/services/validation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ const isValidGitHubBlobUrl = (url: string): boolean => {
parsedUrl.pathname.split('/')[3] === 'blob'
);
} catch (error) {
// This is expected for non-URL strings, just log and return false
console.debug(`Invalid URL format for GitHub blob check: ${url}`);
return false;
}
};
Expand All @@ -71,7 +73,7 @@ const convertGitHubWebUrl = (url: string): string => {
// Handle GitHub web URLs like: https://github.com/owner/repo/blob/branch/path
// eslint-disable-next-line no-useless-escape
const githubWebPattern = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/(.+)$/;
const match = urlWithoutFragment.match(githubWebPattern);
const match = githubWebPattern.exec(urlWithoutFragment);

if (match) {
const [, owner, repo, branch, filePath] = match;
Expand All @@ -81,6 +83,67 @@ const convertGitHubWebUrl = (url: string): string => {
return url;
};

/**
* Helper function to fetch URL and handle errors
*/
const fetchWithErrorHandling = async (
url: string,
headers: Record<string, string>,
errorMessage: string
): Promise<Response> => {
const res = await fetch(url, { headers });
if (!res.ok) {
throw new Error(`${errorMessage}: ${url} - ${res.statusText}`);
}
return res;
};

/**
* Handle GitHub API URLs
*/
const handleGitHubApiUrl = async (
url: string,
headers: Record<string, string>
): Promise<string> => {
headers['Accept'] = 'application/vnd.github.v3+json';
const res = await fetchWithErrorHandling(url, headers, 'Failed to fetch GitHub API URL');
const fileInfo = (await res.json()) as GitHubFileInfo;

if (!fileInfo.download_url) {
throw new Error(`No download URL found in GitHub API response for: ${url}`);
}

const contentRes = await fetchWithErrorHandling(
fileInfo.download_url,
headers,
'Failed to fetch content from download URL'
);
return await contentRes.text();
};

/**
* Handle raw GitHub content URLs
*/
const handleRawGitHubUrl = async (
url: string,
headers: Record<string, string>
): Promise<string> => {
headers['Accept'] = 'application/vnd.github.v3.raw';
const res = await fetchWithErrorHandling(url, headers, 'Failed to fetch GitHub URL');
return await res.text();
};

/**
* Handle regular HTTP/HTTPS URLs
*/
const handleRegularUrl = async (
url: string,
headers: Record<string, string>
): Promise<string> => {
const res = await fetchWithErrorHandling(url, headers, 'Failed to fetch URL');
return await res.text();
};

/**
* Custom resolver for private repositories
*/
Expand Down Expand Up @@ -108,43 +171,12 @@ const createHttpWithAuthResolver = () => ({
}

if (url.includes('api.github.com')) {
headers['Accept'] = 'application/vnd.github.v3+json';
const res = await fetch(url, { headers });
if (!res.ok) {
throw new Error(
`Failed to fetch GitHub API URL: ${url} - ${res.statusText}`
);
}
const fileInfo = (await res.json()) as GitHubFileInfo;

if (fileInfo.download_url) {
const contentRes = await fetch(fileInfo.download_url, { headers });
if (!contentRes.ok) {
throw new Error(
`Failed to fetch content from download URL: ${fileInfo.download_url} - ${contentRes.statusText}`
);
}
return await contentRes.text();
}
throw new Error(
`No download URL found in GitHub API response for: ${url}`
);
} else if (url.includes('raw.githubusercontent.com')) {
headers['Accept'] = 'application/vnd.github.v3.raw';
const res = await fetch(url, { headers });
if (!res.ok) {
throw new Error(
`Failed to fetch GitHub URL: ${url} - ${res.statusText}`
);
}
return await res.text();
} else {
const res = await fetch(url, { headers });
if (!res.ok) {
throw new Error(`Failed to fetch URL: ${url} - ${res.statusText}`);
}
return await res.text();
return handleGitHubApiUrl(url, headers);
}
if (url.includes('raw.githubusercontent.com')) {
return handleRawGitHubUrl(url, headers);
}
return handleRegularUrl(url, headers);
},
});

Expand Down