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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Context } from "../../../core/command/cli-context";
import { ActionFlowApi } from "./action-flow-api";
import { fileService, FileService } from "../../../core/utils/file-service";
import { logger } from "../../../core/utils/logger";
import { FileConstants } from "../../../core/utils/file.constants";

export class ActionFlowService {
public static readonly METADATA_FILE_NAME = "metadata.json";
Expand All @@ -22,15 +23,15 @@ export class ActionFlowService {

const zip = new AdmZip();
tmpZip.getEntries().forEach(entry => {
zip.addFile(entry.entryName, entry.getData());
zip.addFile(entry.entryName, entry.getData(), "", FileConstants.DEFAULT_FILE_PERMISSIONS);
});

if (metadataFilePath) {
this.attachMetadataFile(metadataFilePath, zip);
}

const fileName = "action-flows_export_" + uuidv4() + ".zip";
zip.writeZip(fileName);
zip.writeZip(fileName, () => fs.chmodSync(fileName, FileConstants.DEFAULT_FILE_PERMISSIONS));
logger.info(FileService.fileDownloadedMessage + fileName);
}

Expand Down Expand Up @@ -74,6 +75,6 @@ export class ActionFlowService {
fileName = fileName + (fileName.endsWith(".json") ? "" : ".json");
const metadata = fileService.readFile(fileName);

zip.addFile(ActionFlowService.METADATA_FILE_NAME, Buffer.from(metadata));
zip.addFile(ActionFlowService.METADATA_FILE_NAME, Buffer.from(metadata), "", FileConstants.DEFAULT_FILE_PERMISSIONS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BatchImportExportApi } from "./api/batch-import-export-api";
import { StudioService } from "./studio.service";
import { GitService } from "../../core/git-profile/git/git.service";
import * as fs from "fs";
import { FileConstants } from "../../core/utils/file.constants";

export class BatchImportExportService {

Expand Down Expand Up @@ -82,13 +83,13 @@ export class BatchImportExportService {

let exportedVariables = await this.getVersionedVariablesForPackagesWithKeys(versionsByPackageKey);
exportedVariables = this.studioService.fixConnectionVariables(exportedVariables);
exportedPackagesZip.addFile(BatchExportImportConstants.VARIABLES_FILE_NAME, Buffer.from(stringify(exportedVariables), "utf8"));
exportedPackagesZip.addFile(BatchExportImportConstants.VARIABLES_FILE_NAME, Buffer.from(stringify(exportedVariables), "utf8"), "", FileConstants.DEFAULT_FILE_PERMISSIONS);

const studioPackageKeys = manifest.filter(packageManifest => packageManifest.flavor === BatchExportImportConstants.STUDIO)
.map(packageManifest => packageManifest.packageKey);

const studioData = await this.studioService.getStudioPackageManifests(studioPackageKeys);
exportedPackagesZip.addFile(BatchExportImportConstants.STUDIO_FILE_NAME, Buffer.from(stringify(studioData), "utf8"));
exportedPackagesZip.addFile(BatchExportImportConstants.STUDIO_FILE_NAME, Buffer.from(stringify(studioData), "utf8"), "", FileConstants.DEFAULT_FILE_PERMISSIONS);

exportedPackagesZip.getEntries().forEach(entry => {
if (entry.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) {
Expand Down Expand Up @@ -126,8 +127,10 @@ export class BatchImportExportService {

public async batchImportPackages(sourcePath: string, overwrite: boolean, gitBranch: string, performValidation: boolean = false): Promise<void> {
let sourceToBeImported: string;
let temporaryGitFolder: string;
if (gitBranch) {
sourceToBeImported = await this.gitService.pullFromBranch(gitBranch);
temporaryGitFolder = sourceToBeImported;
} else {
sourceToBeImported = sourcePath;
}
Expand All @@ -147,8 +150,9 @@ export class BatchImportExportService {
await this.studioService.processImportedPackages(configs, existingStudioPackages, studioManifests);

if (gitBranch) {
fs.rmSync(sourceToBeImported);
fs.rmSync(temporaryGitFolder, { recursive: true });
}
fs.rmSync(sourceToBeImported);

const reportFileName = "config_import_report_" + uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(postPackageImportData), reportFileName);
Expand Down Expand Up @@ -258,7 +262,7 @@ export class BatchImportExportService {
} else {
const fileDownloadedMessage = "File downloaded successfully. New filename: ";
const filename = `export_${uuidv4()}.zip`;
exportedZip.writeZip(filename);
exportedZip.writeZip(filename, () => fs.chmodSync(filename, FileConstants.DEFAULT_FILE_PERMISSIONS));
logger.info(fileDownloadedMessage + filename);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/commands/studio/manager/widget.manager-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FatalError, logger } from "../../../core/utils/logger";
import { Context } from "../../../core/command/cli-context";
import * as AdmZip from "adm-zip";
import { parse } from "../../../core/utils/yaml";
import { FileConstants } from "../../../core/utils/file.constants";

export class WidgetManagerFactory {

Expand Down Expand Up @@ -34,7 +35,7 @@ export class WidgetManagerFactory {
const zip = new AdmZip();
const zipFileName = path.resolve(process.cwd(), "output.zip");
zip.addLocalFolder(path.resolve(process.cwd()));
zip.writeZip(zipFileName);
zip.writeZip(zipFileName, () => fs.chmodSync(zipFileName, FileConstants.DEFAULT_FILE_PERMISSIONS));
return fs.createReadStream(path.resolve(process.cwd(), "output.zip"));
}

Expand Down
6 changes: 4 additions & 2 deletions src/core/git-profile/git-profile.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FatalError, logger } from "../utils/logger";
import os = require("os");
import { AuthenticationType, GitProfile } from "./git-profile.interface";
import { GitProfileValidator } from "./git-profile.validator";
import { FileConstants } from "../utils/file.constants";

export interface GitConfig {
defaultProfile: string;
Expand Down Expand Up @@ -63,6 +64,7 @@ export class GitProfileService {
const newProfileFileName = this.constructProfileFileName(profile.name);
fs.writeFileSync(path.resolve(this.gitProfileContainerPath, newProfileFileName), JSON.stringify(profile), {
encoding: "utf-8",
mode: FileConstants.DEFAULT_FILE_PERMISSIONS,
});
}

Expand All @@ -88,12 +90,12 @@ export class GitProfileService {
}

private storeConfig(config: GitConfig): void {
fs.writeFileSync(this.configContainer, JSON.stringify(config), { encoding: "utf-8" });
fs.writeFileSync(this.configContainer, JSON.stringify(config), { encoding: "utf-8", mode: FileConstants.DEFAULT_FILE_PERMISSIONS });
}

private createProfileContainerIfNotExists(): void {
if (!fs.existsSync(this.gitProfileContainerPath)) {
fs.mkdirSync(this.gitProfileContainerPath);
fs.mkdirSync(this.gitProfileContainerPath, FileConstants.DEFAULT_FOLDER_PERMISSIONS);
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/core/git-profile/git/git.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Context } from "../../command/cli-context";
import { v4 as uuid } from "uuid";
import { SimpleGit } from "simple-git/dist/typings/simple-git";
import { GitProfile } from "../git-profile.interface";
import { FileConstants } from "../../utils/file.constants";

export class GitService {

Expand All @@ -20,7 +21,7 @@ export class GitService {
const repoUrl = this.getRepoUrl();

const targetDir = path.join(os.tmpdir(), `content-cli-${uuid()}`);
fs.mkdirSync(targetDir, { recursive: true });
fs.mkdirSync(targetDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });

const git = simpleGit();
try {
Expand All @@ -36,7 +37,7 @@ export class GitService {
this.validateGitProfileExistence();

const workingDir = path.join(os.tmpdir(), `content-cli-${uuid()}`);
fs.mkdirSync(workingDir, { recursive: true });
fs.mkdirSync(workingDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });

const repoUrl = this.getRepoUrl();
const git = simpleGit();
Expand Down
4 changes: 3 additions & 1 deletion src/core/http/http-shared/base.manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FatalError, logger } from "../../utils/logger";
import { ManagerConfig } from "./manager-config.interface";
import { HttpClient } from "../http-client";
import { Context } from "../../command/cli-context";
import { FileConstants } from "../../utils/file.constants";

export abstract class BaseManager {
private httpClient: () => HttpClient;
Expand Down Expand Up @@ -103,13 +104,14 @@ export abstract class BaseManager {

protected writeStreamToFile(data: any): string {
const filename = this.getConfig().exportFileName;
fs.writeFileSync(filename, data);
fs.writeFileSync(filename, data, { mode: FileConstants.DEFAULT_FILE_PERMISSIONS });
return filename;
}

protected writeToFileWithGivenName(data: any, filename: string): void {
fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), {
encoding: "utf-8",
mode: FileConstants.DEFAULT_FILE_PERMISSIONS,
});
}

Expand Down
6 changes: 4 additions & 2 deletions src/core/profile/profile.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Client, Issuer } from "openid-client";
import axios from "axios";
import os = require("os");
import { SecureSecretStorageService } from "./secret-storage.service";
import { FileConstants } from "../utils/file.constants";

const homedir = os.homedir();
// use 5 seconds buffer to avoid rare cases when accessToken is just about to expire before the command is sent
Expand Down Expand Up @@ -117,6 +118,7 @@ export class ProfileService {

fs.writeFileSync(path.resolve(this.profileContainerPath, newProfileFileName), JSON.stringify(profileToStore), {
encoding: "utf-8",
mode: FileConstants.DEFAULT_FILE_PERMISSIONS,
});
}

Expand All @@ -134,12 +136,12 @@ export class ProfileService {
}

private storeConfig(config: Config): void {
fs.writeFileSync(this.configContainer, JSON.stringify(config), { encoding: "utf-8" });
fs.writeFileSync(this.configContainer, JSON.stringify(config), { encoding: "utf-8", mode: FileConstants.DEFAULT_FILE_PERMISSIONS });
}

private createProfileContainerIfNotExists(): void {
if (!fs.existsSync(this.profileContainerPath)) {
fs.mkdirSync(this.profileContainerPath);
fs.mkdirSync(this.profileContainerPath, FileConstants.DEFAULT_FOLDER_PERMISSIONS);
}
}

Expand Down
31 changes: 23 additions & 8 deletions src/core/utils/file-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {FatalError, logger} from "./logger";
import AdmZip = require("adm-zip");
import { v4 as uuidv4 } from "uuid";
import * as os from "node:os";
import { FileConstants } from "./file.constants";

export class FileService {
public static readonly fileDownloadedMessage = "File downloaded successfully. New filename: ";

public writeToFileWithGivenName(data: any, filename: string): void {
fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), {
encoding: "utf-8",
mode: FileConstants.DEFAULT_FILE_PERMISSIONS,
});
}

Expand All @@ -29,13 +31,13 @@ export class FileService {

public extractExportedZipWithNestedZips(zipFile: AdmZip): string {
const tempDir = path.join(os.tmpdir(), `${uuidv4()}`);
fs.mkdirSync(tempDir, { recursive: true });

return this.extractExportedZipWithNestedZipsToDir(zipFile, tempDir);
}

public extractExportedZipWithNestedZipsToDir(zipFile: AdmZip, targetDir: string): string {
zipFile.extractAllTo(targetDir, true);
fs.mkdirSync(targetDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });
zipFile.extractAllTo(targetDir, true, true);

const files = fs.readdirSync(targetDir);
for (const file of files) {
Expand All @@ -44,11 +46,12 @@ export class FileService {
const nestedZip = new AdmZip(innerZipPath);
const nestedDir = innerZipPath.replace(/\.zip$/, "");

fs.mkdirSync(nestedDir, { recursive: true });
nestedZip.extractAllTo(nestedDir, true);
fs.mkdirSync(nestedDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });
nestedZip.extractAllTo(nestedDir, true, true);
fs.rmSync(innerZipPath); // Optionally remove the inner zip
}
}
this.restrictFilePermissions(targetDir);
return targetDir;
}

Expand All @@ -73,17 +76,16 @@ export class FileService {
zip.addLocalFolder(fullPath);
const zippedBuffer = zip.toBuffer();

finalZip.addFile(`${file}.zip`, zippedBuffer);
finalZip.addFile(`${file}.zip`, zippedBuffer, "", FileConstants.DEFAULT_FILE_PERMISSIONS);
} else if (stat.isFile()) {
finalZip.addLocalFile(fullPath);
}
});

const tempDir = path.join(os.tmpdir(), "content-cli-exports");
fs.mkdirSync(tempDir, { recursive: true });

fs.mkdirSync(tempDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });
const zipFilePath = path.join(tempDir, `export_${uuidv4()}.zip`);
finalZip.writeZip(zipFilePath);
finalZip.writeZip(zipFilePath, () => fs.chmodSync(zipFilePath, FileConstants.DEFAULT_FILE_PERMISSIONS));

return zipFilePath;
}
Expand All @@ -92,6 +94,19 @@ export class FileService {
private getSerializedFileContent(data: any): string {
return data;
}

private restrictFilePermissions(targetDir: string): void {
const files = fs.readdirSync(targetDir);
for (const file of files) {
const filePath = path.join(targetDir, file);
if (fs.statSync(filePath)?.isDirectory()) {
fs.chmodSync(filePath, FileConstants.DEFAULT_FOLDER_PERMISSIONS);
this.restrictFilePermissions(filePath);
} else {
fs.chmodSync(filePath, FileConstants.DEFAULT_FILE_PERMISSIONS);
}
}
}
}

export const fileService = new FileService();
4 changes: 4 additions & 0 deletions src/core/utils/file.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum FileConstants {
DEFAULT_FILE_PERMISSIONS = 0o600,
DEFAULT_FOLDER_PERMISSIONS = 0o700
}
3 changes: 2 additions & 1 deletion src/core/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Logger } from "winston";
import os = require("os");
import * as path from "path";
import * as fs from "fs";
import { FileConstants } from "./file.constants";

class CustomTransport extends Transport {
constructor(opts: any) {
Expand Down Expand Up @@ -37,7 +38,7 @@ const maxSizeBytes = maxLogSizeMB * 1024 * 1024; // 3 MB in bytes
// --- Ensure log directory exists ---
try {
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
fs.mkdirSync(logDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });
}
} catch (error) {
console.error(`Error creating log directory: ${logDir}`, error);
Expand Down
2 changes: 1 addition & 1 deletion tests/commands/action-flows/analyze-action-flows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe("Analyze action-flows", () => {
expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage);
const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1];

expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockAnalyzeResponse, null, 4), { encoding: "utf-8" });
expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockAnalyzeResponse, null, 4), { encoding: "utf-8", mode: 0o600 });
expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets/analyze`, expect.anything());
});
});
2 changes: 1 addition & 1 deletion tests/commands/action-flows/import-action-flows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe("Import action-flows", () => {
expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage);
const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1];

expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockImportResponse, null, 4), { encoding: "utf-8" });
expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockImportResponse, null, 4), { encoding: "utf-8", mode: 0o600 });
expect(mockedAxiosInstance.post).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/import/assets`, expect.anything(), expect.anything());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("Asset registry examples", () => {
expect(mockWriteFileSync).toHaveBeenCalledWith(
path.resolve(process.cwd(), expectedFileName),
expect.any(String),
{ encoding: "utf-8" }
{ encoding: "utf-8", mode: 0o600 }
);

const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]);
Expand Down
2 changes: 1 addition & 1 deletion tests/commands/asset-registry/asset-registry-get.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe("Asset registry get", () => {
expect(mockWriteFileSync).toHaveBeenCalledWith(
path.resolve(process.cwd(), expectedFileName),
expect.any(String),
{ encoding: "utf-8" }
{ encoding: "utf-8", mode: 0o600 }
);

const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as AssetRegistryDescriptor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe("Asset registry list", () => {
expect(mockWriteFileSync).toHaveBeenCalledWith(
path.resolve(process.cwd(), expectedFileName),
expect.any(String),
{ encoding: "utf-8" }
{ encoding: "utf-8", mode: 0o600 }
);

const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as AssetRegistryMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe("Asset registry methodology", () => {
expect(mockWriteFileSync).toHaveBeenCalledWith(
path.resolve(process.cwd(), expectedFileName),
expect.any(String),
{ encoding: "utf-8" }
{ encoding: "utf-8", mode: 0o600 }
);

const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe("Asset registry schema", () => {
expect(mockWriteFileSync).toHaveBeenCalledWith(
path.resolve(process.cwd(), expectedFileName),
expect.any(String),
{ encoding: "utf-8" }
{ encoding: "utf-8", mode: 0o600 }
);

const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]);
Expand Down
Loading
Loading