diff --git a/.changeset/1884.md b/.changeset/1884.md new file mode 100644 index 00000000..4ab13b8b --- /dev/null +++ b/.changeset/1884.md @@ -0,0 +1,32 @@ +--- +'@asyncapi/cli': patch +--- + +fix: fixed all sonar cloud issue + +- aa5ccef: fix(config): +- use 'node:' prefix for built-in modules +- replace if-assignment with nullish coalescing (??=) +- use String#replaceAll and String.raw in wildcardToRegex +- eb7394e: refactor: improve GitHub URL handling and simplify HTTP resolver + +Use RegExp.exec() instead of String.match() in convertGitHubWebUrl + +Remove unnecessary try/catch from isValidGitHubBlobUrl + +Refactor read function in HTTP resolver to reduce cognitive complexity from 16 to 15 +- 35b917d: Refactor URL conversion to use RegExp.exec() instead of match() +- 05be198: chore: updated the validation.service.ts +- 2bd1f07: fix: address Sonar S7780 by replacing manual escaping with String.raw +- b9cf80a: chore: pull the changes +- ba5f736: replaceAll rule in wildcardToRegex function +- c3b7224: fix(config): replace unsafe regex literal with safe escapePattern to satisfy Sonar rule S7780 +- 538dea1: replaceAll rule in wildcardToRegex function + +chore: add changeset for PR #1884 + +fix(config): replace unsafe regex literal with safe escapePattern to satisfy Sonar rule S7780 +- 0fe8c37: fix(config): replace unsafe regex literal with safe escapePattern to satisfy Sonar rule S7780 +- b5ee095: chore: pulling from master + + diff --git a/src/domains/services/config.service.ts b/src/domains/services/config.service.ts index b5cf3497..f8bb88f7 100644 --- a/src/domains/services/config.service.ts +++ b/src/domains/services/config.service.ts @@ -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'); @@ -51,9 +51,7 @@ export class ConfigService { */ static async addAuthEntry(entry: AuthEntry): Promise { const config = await this.loadConfig(); - if (!config.auth) { - config.auth = []; - } + config.auth ??= []; config.auth.push(entry); await this.saveConfig(config); } @@ -96,13 +94,21 @@ export class ConfigService { * @param pattern - wildcard pattern */ private static wildcardToRegex(pattern: string): RegExp { - const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&'); - + const rawPattern = String.raw`${pattern}`; + // Sonar-safe regex escaping using String.raw + const escapePattern = '[.+?^${}()|[\\]\\\\]'; + const escaped = rawPattern.replaceAll( + new RegExp(escapePattern, 'g'), + String.raw`\$&` + ); + // Convert wildcards: + // ** -> match any depth + // * -> match one segment const regexStr = escaped - .replace(/\*\*/g, '.*') - .replace(/\*/g, '[^/]*'); + .replaceAll('**', '.*') // ** -> .* + .replaceAll('*', '[^/]*'); // * -> any chars except '/' // eslint-disable-next-line security/detect-non-literal-regexp - return new RegExp(`^${regexStr}`); + return new RegExp(`^${regexStr}$`); } } diff --git a/src/domains/services/validation.service.ts b/src/domains/services/validation.service.ts index a2fcc4e3..f89d430f 100644 --- a/src/domains/services/validation.service.ts +++ b/src/domains/services/validation.service.ts @@ -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; } }; @@ -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; @@ -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, + errorMessage: string +): Promise => { + 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 +): Promise => { + 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 +): Promise => { + 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 +): Promise => { + const res = await fetchWithErrorHandling(url, headers, 'Failed to fetch URL'); + return await res.text(); +}; + /** * Custom resolver for private repositories */ @@ -104,47 +167,16 @@ const createHttpWithAuthResolver = () => ({ if (authInfo) { headers['Authorization'] = `${authInfo.authType} ${authInfo.token}`; - Object.assign(headers, authInfo.headers); // merge custom headers + Object.assign(headers, authInfo.headers); } 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); }, });