This commit enhances application security against potential attacks by isolating dependencies that access the host system (like file operations) from the renderer process. It narrows the exposed functionality to script execution only, adding an extra security layer. The changes allow secure and scalable API exposure, preparing for future functionalities such as desktop notifications for script errors (#264), improved script execution handling (#296), and creating restore points (#50) in a secure and repeatable way. Changes include: - Inject `CodeRunner` into Vue components via dependency injection. - Move `CodeRunner` to the application layer as an abstraction for better domain-driven design alignment. - Refactor `SystemOperations` and related interfaces, removing the `I` prefix. - Update architecture documentation for clarity. - Update return types in `NodeSystemOperations` to match the Node APIs. - Improve `WindowVariablesProvider` integration tests for better error context. - Centralize type checks with common functions like `isArray` and `isNumber`. - Change `CodeRunner` to use `os` parameter, ensuring correct window variable injection. - Streamline API exposure to the renderer process: - Automatically bind function contexts to prevent loss of original context. - Implement a way to create facades (wrapper/proxy objects) for increased security.
109 lines
3.5 KiB
TypeScript
109 lines
3.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { ApiFacadeFactory, provideWindowVariables } from '@/presentation/electron/preload/ContextBridging/RendererApiProvider';
|
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
|
import { Logger } from '@/application/Common/Log/Logger';
|
|
import { LoggerStub } from '@tests/unit/shared/Stubs/LoggerStub';
|
|
import { CodeRunner } from '@/application/CodeRunner';
|
|
import { CodeRunnerStub } from '@tests/unit/shared/Stubs/CodeRunnerStub';
|
|
import { PropertyKeys } from '@/TypeHelpers';
|
|
import { WindowVariables } from '@/infrastructure/WindowVariables/WindowVariables';
|
|
|
|
describe('RendererApiProvider', () => {
|
|
describe('provideWindowVariables', () => {
|
|
interface WindowVariableTestCase {
|
|
readonly description: string;
|
|
setupContext(context: RendererApiProviderTestContext): RendererApiProviderTestContext;
|
|
readonly expectedValue: unknown;
|
|
}
|
|
const testScenarios: Record<PropertyKeys<Required<WindowVariables>>, WindowVariableTestCase> = {
|
|
isDesktop: {
|
|
description: 'returns true',
|
|
setupContext: (context) => context,
|
|
expectedValue: true,
|
|
},
|
|
codeRunner: (() => {
|
|
const codeRunner = new CodeRunnerStub();
|
|
const createFacadeMock: ApiFacadeFactory = (obj) => obj;
|
|
return {
|
|
description: 'encapsulates correctly',
|
|
setupContext: (context) => context
|
|
.withCodeRunner(codeRunner)
|
|
.withApiFacadeCreator(createFacadeMock),
|
|
expectedValue: codeRunner,
|
|
};
|
|
})(),
|
|
os: (() => {
|
|
const operatingSystem = OperatingSystem.WindowsPhone;
|
|
return {
|
|
description: 'returns expected',
|
|
setupContext: (context) => context.withOs(operatingSystem),
|
|
expectedValue: operatingSystem,
|
|
};
|
|
})(),
|
|
log: (() => {
|
|
const logger = new LoggerStub();
|
|
const createFacadeMock: ApiFacadeFactory = (obj) => obj;
|
|
return {
|
|
description: 'encapsulates correctly',
|
|
setupContext: (context) => context
|
|
.withLogger(logger)
|
|
.withApiFacadeCreator(createFacadeMock),
|
|
expectedValue: logger,
|
|
};
|
|
})(),
|
|
};
|
|
Object.entries(testScenarios).forEach((
|
|
[property, { description, setupContext, expectedValue }],
|
|
) => {
|
|
it(`${property}: ${description}`, () => {
|
|
// arrange
|
|
const testContext = setupContext(new RendererApiProviderTestContext());
|
|
// act
|
|
const variables = testContext.provideWindowVariables();
|
|
// assert
|
|
const actualValue = variables[property];
|
|
expect(actualValue).to.equal(expectedValue);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
class RendererApiProviderTestContext {
|
|
private codeRunner: CodeRunner = new CodeRunnerStub();
|
|
|
|
private os: OperatingSystem = OperatingSystem.Android;
|
|
|
|
private log: Logger = new LoggerStub();
|
|
|
|
private apiFacadeCreator: ApiFacadeFactory = (obj) => obj;
|
|
|
|
public withCodeRunner(codeRunner: CodeRunner): this {
|
|
this.codeRunner = codeRunner;
|
|
return this;
|
|
}
|
|
|
|
public withOs(os: OperatingSystem): this {
|
|
this.os = os;
|
|
return this;
|
|
}
|
|
|
|
public withLogger(log: Logger): this {
|
|
this.log = log;
|
|
return this;
|
|
}
|
|
|
|
public withApiFacadeCreator(apiFacadeCreator: ApiFacadeFactory): this {
|
|
this.apiFacadeCreator = apiFacadeCreator;
|
|
return this;
|
|
}
|
|
|
|
public provideWindowVariables() {
|
|
return provideWindowVariables(
|
|
() => this.codeRunner,
|
|
() => this.log,
|
|
() => this.os,
|
|
this.apiFacadeCreator,
|
|
);
|
|
}
|
|
}
|