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

Merging of input files (Backend logic) #2796

Merged
merged 3 commits into from
Feb 18, 2025
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
75 changes: 5 additions & 70 deletions src/ElectronBackend/main/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { BrowserWindow, shell, WebContents } from 'electron';
import fs from 'fs';
import path from 'path';
import upath from 'upath';
import zlib from 'zlib';

import { legacyOutputFileEnding } from '../../Frontend/shared-constants';
import { AllowedFrontendChannels } from '../../shared/ipc-channels';
import {
ExportArgsType,
Expand All @@ -33,10 +33,7 @@ import {
} from '../errorHandling/errorHandling';
import { loadInputAndOutputFromFilePath } from '../input/importFromFile';
import { serializeAttributions } from '../input/parseInputData';
import {
convertOwaspToOpossum,
convertScancodeToOpossum,
} from '../opossum-file/convertToOpossum';
import { convertToOpossum } from '../opossum-file/opossum-file';
import { writeCsvToFile } from '../output/writeCsvToFile';
import { writeSpdxFile } from '../output/writeSpdxFile';
import { GlobalBackendState, OpossumOutputFile } from '../types/types';
Expand All @@ -54,10 +51,6 @@ import {
} from './globalBackendState';
import logger from './logger';

const outputFileEnding = '_attributions.json';
const jsonGzipFileExtension = '.json.gz';
const jsonFileExtension = '.json';

export function getSaveFileListener(
mainWindow: BrowserWindow,
): (_: unknown, args: SaveFileArgs) => Promise<void> {
Expand Down Expand Up @@ -213,27 +206,8 @@ export function getImportFileConvertAndLoadListener(
throw new Error('Output directory does not exist');
}

logger.info('Converting .json to .opossum format');

if (resourceFilePath.endsWith(outputFileEnding)) {
resourceFilePath = tryToGetInputFileFromOutputFile(resourceFilePath);
}

switch (fileType) {
case FileType.LEGACY_OPOSSUM:
await writeOpossumFile({
path: opossumFilePath,
input: getInputJson(resourceFilePath),
output: getOutputJson(resourceFilePath),
});
break;
case FileType.SCANCODE_JSON:
await convertScancodeToOpossum(resourceFilePath, opossumFilePath);
break;
case FileType.OWASP_JSON:
await convertOwaspToOpossum(resourceFilePath, opossumFilePath);
break;
}
logger.info('Converting input file to .opossum format');
await convertToOpossum(resourceFilePath, opossumFilePath, fileType);

logger.info('Updating global backend state');
initializeGlobalBackendState(opossumFilePath, true);
Expand All @@ -255,7 +229,7 @@ function initializeGlobalBackendState(
resourceFilePath: isOpossumFormat ? undefined : filePath,
attributionFilePath: isOpossumFormat
? undefined
: getFilePathWithAppendix(filePath, outputFileEnding),
: getFilePathWithAppendix(filePath, legacyOutputFileEnding),
opossumFilePath: isOpossumFormat ? filePath : undefined,
followUpFilePath: getFilePathWithAppendix(filePath, '_follow_up.csv'),
compactBomFilePath: getFilePathWithAppendix(
Expand Down Expand Up @@ -316,17 +290,6 @@ function formatBaseURL(baseURL: string): string {
return `file://${baseURL}/{path}`;
}

function tryToGetInputFileFromOutputFile(filePath: string): string {
const outputFilePattern = `(${outputFileEnding})$`;
const outputFileRegex = new RegExp(outputFilePattern);

return fs.existsSync(filePath.replace(outputFileRegex, jsonFileExtension))
? filePath.replace(outputFileRegex, jsonFileExtension)
: fs.existsSync(filePath.replace(outputFileRegex, jsonGzipFileExtension))
? filePath.replace(outputFileRegex, jsonGzipFileExtension)
: filePath;
}

export async function openFile(
mainWindow: BrowserWindow,
filePath: string,
Expand Down Expand Up @@ -545,31 +508,3 @@ export function setLoadingState(
isLoading,
});
}

function getInputJson(resourceFilePath: string): string {
let inputJson: string;
if (resourceFilePath.endsWith(jsonGzipFileExtension)) {
const file = fs.readFileSync(resourceFilePath);
inputJson = zlib.gunzipSync(file).toString();
} else {
inputJson = fs.readFileSync(resourceFilePath, {
encoding: 'utf-8',
});
}

return inputJson;
}

function getOutputJson(resourceFilePath: string): string | undefined {
const expectedAssociatedAttributionFilePath = getFilePathWithAppendix(
resourceFilePath,
outputFileEnding,
);
if (fs.existsSync(expectedAssociatedAttributionFilePath)) {
return fs.readFileSync(expectedAssociatedAttributionFilePath, {
encoding: 'utf-8',
});
}

return undefined;
}
30 changes: 30 additions & 0 deletions src/ElectronBackend/opossum-file/ExternalFileConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Meta Platforms, Inc. and its affiliates
// SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
//
// SPDX-License-Identifier: Apache-2.0
import { FileConverter } from './FileConverter';

export abstract class ExternalFileConverter extends FileConverter {
protected override preConvertFile(_: string): Promise<string | null> {
return new Promise((resolve) => resolve(null));
}

override async convertToOpossum(
toBeConvertedFilePath: string,
opossumSaveLocation: string,
): Promise<void> {
try {
await this.execFile(this.OPOSSUM_FILE_EXECUTABLE, [
'generate',
'-o',
opossumSaveLocation,
this.fileTypeSwitch,
toBeConvertedFilePath,
]);
} catch (error) {
throw new Error(
`Conversion of ${this.fileTypeName} file to .opossum file failed`,
);
}
}
}
60 changes: 60 additions & 0 deletions src/ElectronBackend/opossum-file/FileConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Meta Platforms, Inc. and its affiliates
// SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
//
// SPDX-License-Identifier: Apache-2.0
import { execFile as execFileCallback } from 'child_process';
import { app } from 'electron';
import fs from 'fs';
import { join } from 'path';
import { promisify } from 'util';

export abstract class FileConverter {
protected abstract readonly fileTypeSwitch: string;
protected abstract readonly fileTypeName: string;

protected readonly execFile = promisify(execFileCallback);

protected readonly OPOSSUM_FILE_EXECUTABLE = join(
app?.getAppPath?.() ?? './',
process.env.NODE_ENV === 'e2e' ? '../..' : '',
'bin/opossum-file',
);

protected abstract preConvertFile(
toBeConvertedFilePath: string,
): Promise<string | null>;

abstract convertToOpossum(
toBeConvertedFilePath: string,
opossumSaveLocation: string,
): Promise<void>;

async mergeFileIntoOpossum(
toBeConvertedFilePath: string,
opossumFilePath: string,
): Promise<void> {
const preConvertedFilePath = await this.preConvertFile(
toBeConvertedFilePath,
);

try {
await this.execFile(this.OPOSSUM_FILE_EXECUTABLE, [
'generate',
'-o',
opossumFilePath,
this.fileTypeSwitch,
preConvertedFilePath || toBeConvertedFilePath,
'--opossum',
opossumFilePath,
]);

if (preConvertedFilePath) {
fs.rmSync(preConvertedFilePath);
}
} catch (error) {
throw new Error(
`Merging ${this.fileTypeName} file into current file failed`,
);
}
}
}
106 changes: 106 additions & 0 deletions src/ElectronBackend/opossum-file/LegacyFileConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: Meta Platforms, Inc. and its affiliates
// SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
//
// SPDX-License-Identifier: Apache-2.0
import { app } from 'electron';
import fs from 'fs';
import path from 'path';
import zlib from 'zlib';

import { legacyOutputFileEnding } from '../../Frontend/shared-constants';
import { writeOpossumFile } from '../../shared/write-file';
import { getFilePathWithAppendix } from '../utils/getFilePathWithAppendix';
import { FileConverter } from './FileConverter';

export class LegacyFileConverter extends FileConverter {
readonly fileTypeName: string = 'Legacy Opossum';
readonly fileTypeSwitch: string = '--opossum';

private tryToGetLegacyInputJsonFromLegacyOutputJson(
filePath: string,
): string {
const outputFilePattern = `(${legacyOutputFileEnding})$`;
const outputFileRegex = new RegExp(outputFilePattern);

const jsonInputFilePath = filePath.replace(outputFileRegex, '.json');
if (fs.existsSync(jsonInputFilePath)) {
return jsonInputFilePath;
}

const jsonGzipInputFilePath = filePath.replace(outputFileRegex, '.json.gz');
if (fs.existsSync(jsonGzipInputFilePath)) {
return jsonGzipInputFilePath;
}

return filePath;
}

private readInputJson(filePath: string): string {
let inputJson: string;
if (filePath.endsWith('.json.gz')) {
const file = fs.readFileSync(filePath);
inputJson = zlib.gunzipSync(file).toString();
} else {
inputJson = fs.readFileSync(filePath, {
encoding: 'utf-8',
});
}

return inputJson;
}

private readOutputJson(filePath: string): string | undefined {
const expectedAssociatedAttributionFilePath = getFilePathWithAppendix(
filePath,
legacyOutputFileEnding,
);
if (fs.existsSync(expectedAssociatedAttributionFilePath)) {
return fs.readFileSync(expectedAssociatedAttributionFilePath, {
encoding: 'utf-8',
});
}

return undefined;
}

override async convertToOpossum(
toBeConvertedFilePath: string,
opossumSaveLocation: string,
): Promise<void> {
try {
let pathToInputJson = toBeConvertedFilePath;

if (toBeConvertedFilePath.endsWith(legacyOutputFileEnding)) {
pathToInputJson = this.tryToGetLegacyInputJsonFromLegacyOutputJson(
toBeConvertedFilePath,
);
}

await writeOpossumFile({
path: opossumSaveLocation,
input: this.readInputJson(pathToInputJson),
output: this.readOutputJson(pathToInputJson),
});
} catch (error) {
throw new Error(
'Conversion of Legacy Opossum file to .opossum file failed',
);
}
}

protected override async preConvertFile(
toBeConvertedFilePath: string,
): Promise<string | null> {
let tempFilePath;
try {
tempFilePath = path.join(app.getPath('temp'), 'temp.opossum');
} catch (error) {
// When executing as part of unit tests, app.getPath('temp') throws an error
tempFilePath = path.join(__dirname, 'temp.opossum');
}

await this.convertToOpossum(toBeConvertedFilePath, tempFilePath);

return tempFilePath;
}
}

This file was deleted.

17 changes: 17 additions & 0 deletions src/ElectronBackend/opossum-file/__tests__/legacy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"metadata": {
"projectId": "2a58a469-738e-4508-98d3-a27bce6e71f7",
"projectTitle": "Test Title",
"fileCreationDate": "2020-07-23 11:47:13.764544"
},
"resources": {
"index.html": 1
},
"externalAttributions": {},
"resourcesToAttributions": {},
"frequentLicenses": [],
"attributionBreakpoints": [],
"filesWithChildren": [],
"baseUrlsForSources": {},
"externalAttributionSources": {}
}
Binary file not shown.
Loading
Loading