mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-03-25 16:48:50 +08:00
feat(native): record encoding (#14188)
fix #13784 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Start/stop system or meeting recordings with Ogg/Opus artifacts and native start/stop APIs; workspace backup recovery. * **Refactor** * Simplified recording lifecycle and UI flows; native runtime now orchestrates recording/processing and reporting. * **Bug Fixes** * Stronger path validation, safer import/export dialogs, consistent error handling/logging, and retry-safe recording processing. * **Chores** * Added cross-platform native audio capture and Ogg/Opus encoding support. * **Tests** * New unit, integration, and e2e tests for recording, path guards, dialogs, and workspace recovery. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -5,24 +5,46 @@ import { app, net, protocol, session } from 'electron';
|
||||
import cookieParser from 'set-cookie-parser';
|
||||
|
||||
import { anotherHost, mainHost } from '../shared/internal-origin';
|
||||
import { isWindows, resourcesPath } from '../shared/utils';
|
||||
import {
|
||||
isPathInsideBase,
|
||||
isWindows,
|
||||
resolveExistingPathInBase,
|
||||
resolvePathInBase,
|
||||
resourcesPath,
|
||||
} from '../shared/utils';
|
||||
import { buildType, isDev } from './config';
|
||||
import { logger } from './logger';
|
||||
|
||||
const webStaticDir = join(resourcesPath, 'web-static');
|
||||
const devServerBase = process.env.DEV_SERVER_URL;
|
||||
const localWhiteListDirs = [
|
||||
path.resolve(app.getPath('sessionData')).toLowerCase(),
|
||||
path.resolve(app.getPath('temp')).toLowerCase(),
|
||||
path.resolve(app.getPath('sessionData')),
|
||||
path.resolve(app.getPath('temp')),
|
||||
];
|
||||
|
||||
function isPathInWhiteList(filepath: string) {
|
||||
const lowerFilePath = filepath.toLowerCase();
|
||||
return localWhiteListDirs.some(whitelistDir =>
|
||||
lowerFilePath.startsWith(whitelistDir)
|
||||
isPathInsideBase(whitelistDir, filepath, {
|
||||
caseInsensitive: isWindows(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function resolveWhitelistedLocalPath(filepath: string) {
|
||||
for (const whitelistDir of localWhiteListDirs) {
|
||||
try {
|
||||
return await resolveExistingPathInBase(whitelistDir, filepath, {
|
||||
caseInsensitive: isWindows(),
|
||||
label: 'filepath',
|
||||
});
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Invalid filepath');
|
||||
}
|
||||
|
||||
const apiBaseByBuildType: Record<typeof buildType, string> = {
|
||||
stable: 'https://app.affine.pro',
|
||||
beta: 'https://insider.affine.pro',
|
||||
@@ -94,15 +116,14 @@ async function handleFileRequest(request: Request) {
|
||||
// for relative path, load the file in resources
|
||||
if (!isAbsolutePath) {
|
||||
if (urlObject.pathname.split('/').at(-1)?.includes('.')) {
|
||||
// Sanitize pathname to prevent path traversal attacks
|
||||
const decodedPath = decodeURIComponent(urlObject.pathname);
|
||||
const normalizedPath = join(webStaticDir, decodedPath).normalize();
|
||||
if (!normalizedPath.startsWith(webStaticDir)) {
|
||||
// Attempted path traversal - reject by using empty path
|
||||
filepath = join(webStaticDir, '');
|
||||
} else {
|
||||
filepath = normalizedPath;
|
||||
}
|
||||
const decodedPath = decodeURIComponent(urlObject.pathname).replace(
|
||||
/^\/+/,
|
||||
''
|
||||
);
|
||||
filepath = resolvePathInBase(webStaticDir, decodedPath, {
|
||||
caseInsensitive: isWindows(),
|
||||
label: 'filepath',
|
||||
});
|
||||
} else {
|
||||
// else, fallback to load the index.html instead
|
||||
filepath = join(webStaticDir, 'index.html');
|
||||
@@ -113,10 +134,10 @@ async function handleFileRequest(request: Request) {
|
||||
if (isWindows()) {
|
||||
filepath = path.resolve(filepath.replace(/^\//, ''));
|
||||
}
|
||||
// security check if the filepath is within app.getPath('sessionData')
|
||||
if (urlObject.host !== 'local-file' || !isPathInWhiteList(filepath)) {
|
||||
throw new Error('Invalid filepath');
|
||||
}
|
||||
filepath = await resolveWhitelistedLocalPath(filepath);
|
||||
}
|
||||
return net.fetch(pathToFileURL(filepath).toString(), clonedRequest);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user