UNPKG

snyk-nuget-plugin

Version:
184 lines 8.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validate = validate; exports.execute = execute; exports.restore = restore; exports.run = run; exports.getBaseIntermediateOutputPath = getBaseIntermediateOutputPath; exports.publish = publish; const debugModule = require("debug"); const errors_1 = require("../../errors"); const path = require("path"); const subprocess = require("./subprocess"); const fs = require("fs"); const os = require("os"); const debug = debugModule('snyk'); function sanitizePath(filePath) { if (!filePath || typeof filePath !== 'string') { return filePath; } // Handle quoted paths (remove quotes, sanitize, then re-add quotes) let isQuoted = false; let cleanPath = filePath; if ((filePath.startsWith('"') && filePath.endsWith('"')) || (filePath.startsWith("'") && filePath.endsWith("'"))) { isQuoted = true; cleanPath = filePath.slice(1, -1); } // Normalize path separators for cross-platform compatibility cleanPath = cleanPath.replace(/\\/g, '/'); // Replace temp directory paths with <TEMP> (check this BEFORE home directory) const tempDir = os.tmpdir().replace(/\\/g, '/'); if (cleanPath.startsWith(tempDir)) { cleanPath = cleanPath.replace(tempDir, '<TEMP>'); } // Replace home directory paths with <HOME> const homeDir = os.homedir().replace(/\\/g, '/'); if (cleanPath.startsWith(homeDir)) { cleanPath = cleanPath.replace(homeDir, '<HOME>'); } // Replace absolute paths with relative paths when possible try { const cwd = process.cwd().replace(/\\/g, '/'); if (cleanPath.startsWith(cwd)) { cleanPath = path.relative(cwd, cleanPath.replace(/\//g, path.sep)) || '.'; cleanPath = cleanPath.replace(/\\/g, '/'); // Normalize again after path.relative } } catch { // Ignore errors, continue with original path } // Restore quotes if they were present return isQuoted ? `"${cleanPath}"` : cleanPath; } function sanitizeForLogging(value) { if (typeof value === 'string') { return sanitizePath(value); } else if (Array.isArray(value)) { return value.map(sanitizeForLogging); } else if (typeof value === 'object' && value !== null) { const sanitized = {}; for (const [key, val] of Object.entries(value)) { sanitized[key] = sanitizeForLogging(val); } return sanitized; } return value; } async function handle(operation, command, args, projectPath) { debug(`running dotnet command: ${operation}: ${command}`); const options = projectPath ? { cwd: projectPath } : {}; try { return await subprocess.execute(command, args, options); } catch (error) { if (!(typeof error === 'object' && error !== null && 'stdout' in error && 'stderr' in error)) { throw new errors_1.CliCommandError(`dotnet ${operation} failed with error: ${error}. Command: ${command}, Args: ${JSON.stringify(sanitizeForLogging(args))}, Options: ${JSON.stringify(sanitizeForLogging(options))}`); } const message = error.stderr || error.stdout; throw new errors_1.CliCommandError(`dotnet ${operation} failed with error: ${message}. Command: ${command}, Args: ${JSON.stringify(sanitizeForLogging(args))}, Options: ${JSON.stringify(sanitizeForLogging(options))}`); } } async function validate() { const command = 'dotnet'; const args = ['--version']; try { const result = await handle('version', command, args); return result.stdout.trim(); } catch (error) { debug('dotnet tool not found, did you install dotnet core?'); throw error; } } async function execute(args, projectPath) { const command = `dotnet`; try { const result = await handle('execute', command, args, projectPath); return result.stdout.trim(); } catch (error) { debug('dotnet tool not found, did you install dotnet core?'); throw error; } } async function restore(projectPath, workingDirectory) { const command = 'dotnet'; const args = [ 'restore', // Get a larger amount of debugging information to stdout in case something fails. // Useful for customers to attempt self-debugging before raising support requests. '--verbosity', 'normal', `"${projectPath}"`, '--p=MSBuildEnableWorkloadResolver=true;TreatWarningsAsErrors=false;WarningsAsErrors=', ]; await handle('restore', command, args, workingDirectory); return; } async function run(projectPath, options) { const command = 'dotnet'; const args = ['run', '--project', projectPath].concat(options); const response = await handle('run', command, args); const stdout = response.stdout; return stdout.slice(stdout.indexOf('{') !== -1 ? stdout.indexOf('{') : stdout.length); } async function getBaseIntermediateOutputPath(projectPath) { const command = 'dotnet'; const args = [ 'msbuild', '-getProperty:BaseIntermediateOutputPath', `"${projectPath}"`, ]; try { const result = await handle('msbuild-getProperty', command, args); const outputPath = result.stdout.trim(); return outputPath || null; } catch (error) { debug(`Failed to get BaseIntermediateOutputPath for ${projectPath}: ${error}`); return null; } } async function publish(projectPath, targetFramework) { const command = 'dotnet'; const args = ['publish', '--nologo']; // Self-contained: Create all required .dlls for version investigation, don't rely on the environment. args.push('--sc'); // Use the current runtime of whatever platform we are on. // This ensures that .dlls will be packaged containing the runtime assembly versions. // TODO: (OSM-521) if/when we allow this to be dynamic based on user input, remember to change this. args.push('--use-current-runtime'); // If your .csproj file contains multiple <TargetFramework> references, you need to supply which one you want to publish. if (targetFramework) { args.push('--framework'); args.push(targetFramework); } // Define a temporary output dir to use for detecting .dlls to use for runtime version assembly detection. const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `snyk-nuget-plugin-publish-csharp-`)); // Changing the PublishDir a temporary directory. // See https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid#recommended-action // about why we're not using `--output` for this. // Some projects can have <IsPublishable> turned to false, that won't allow `publish` command to generate the binary we // need for resolution, so we're going to force <IsPublishable> to be true. // See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish#msbuild // Some projects can have <PublishSingleFile> turned on, that won't generate the self-container binary we need, // so we're disabling it during our scan. // See https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli // Some projects can have <TreatWarningsAsErrors> tuned on, that will throw errors on any warning, making the project impossible to scan. // Or, they can have a list of warning codes in <WarningsAsErrors> that will do the same thing as above. So we're disabling them. // Some projects may include duplicate files in the publish output due to shared dependencies or multi-targeting, // causing build failures. We're disabling <ErrorOnDuplicatePublishOutputFiles> to allow publish to proceed without errors. args.push(`--p:PublishDir=${tempDir};SnykTest=true;IsPublishable=true;PublishSingleFile=false;TreatWarningsAsErrors=false;ErrorOnDuplicatePublishOutputFiles=false;WarningsAsErrors=`); // The path that contains either some form of project file, or a .sln one. // See: https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish#arguments args.push(`"${projectPath}"`); await handle('publish', command, args); return tempDir; } //# sourceMappingURL=dotnet.js.map