mirror of
https://github.com/zadam/trilium.git
synced 2026-04-05 19:49:02 +02:00
Compare commits
24 Commits
experiment
...
release/v0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf23439792 | ||
|
|
b7a0bc08be | ||
|
|
9d6a26dda9 | ||
|
|
a01ce2c3fc | ||
|
|
13b1e0afbb | ||
|
|
4a48796142 | ||
|
|
9a4fef80b9 | ||
|
|
79dc4b39f1 | ||
|
|
9bc18b774e | ||
|
|
465c36407c | ||
|
|
b99486259e | ||
|
|
ecf5475966 | ||
|
|
90822cc8a3 | ||
|
|
5c46209ddc | ||
|
|
176de87b6b | ||
|
|
7f199c527b | ||
|
|
2432e230c5 | ||
|
|
fc1be0d23d | ||
|
|
626aca5181 | ||
|
|
8204322b46 | ||
|
|
ed3b86cd49 | ||
|
|
b371675494 | ||
|
|
ff06c8e7bd | ||
|
|
8ff41d8fa9 |
8
.github/workflows/dev.yml
vendored
8
.github/workflows/dev.yml
vendored
@@ -1,9 +1,13 @@
|
||||
name: Dev
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches:
|
||||
- main
|
||||
- "release/*"
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
branches:
|
||||
- main
|
||||
- "release/*"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
84
SECURITY.md
84
SECURITY.md
@@ -2,13 +2,87 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
In the (still active) 0.X phase of the project only the latest stable minor release is getting bugfixes (including security ones).
|
||||
Only the latest stable minor release receives security fixes.
|
||||
|
||||
So e.g. if the latest stable version is 0.42.3 and the latest beta version is 0.43.0-beta, then 0.42 line will still get security fixes but older versions (like 0.41.X) won't get any fixes.
|
||||
For example, if the latest stable version is 0.92.3 and the latest beta is 0.93.0-beta, then only the 0.92.x line will receive security patches. Older versions (like 0.91.x) will not receive fixes.
|
||||
|
||||
Description above is a general rule and may be altered on case by case basis.
|
||||
This policy may be altered on a case-by-case basis for critical vulnerabilities.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
* For low severity vulnerabilities, they can be reported as GitHub issues.
|
||||
* For severe vulnerabilities, please report it using [GitHub Security Advisories](https://github.com/TriliumNext/Trilium/security/advisories).
|
||||
**Please report all security vulnerabilities through [GitHub Security Advisories](https://github.com/TriliumNext/Notes/security/advisories/new).**
|
||||
|
||||
We do not accept security reports via email, public issues, or other channels. GitHub Security Advisories allows us to:
|
||||
- Discuss and triage vulnerabilities privately
|
||||
- Coordinate fixes before public disclosure
|
||||
- Credit reporters appropriately
|
||||
- Publish advisories with CVE identifiers
|
||||
|
||||
### What to Include
|
||||
|
||||
When reporting, please provide:
|
||||
- A clear description of the vulnerability
|
||||
- Steps to reproduce or proof-of-concept
|
||||
- Affected versions (if known)
|
||||
- Potential impact assessment
|
||||
- Any suggested mitigations or fixes
|
||||
|
||||
### Response Timeline
|
||||
|
||||
- **Initial response**: Within 7 days
|
||||
- **Triage decision**: Within 14 days
|
||||
- **Fix timeline**: Depends on severity and complexity
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
|
||||
- Remote code execution
|
||||
- Authentication/authorization bypass
|
||||
- Cross-site scripting (XSS) that affects other users
|
||||
- SQL injection
|
||||
- Path traversal
|
||||
- Sensitive data exposure
|
||||
- Privilege escalation
|
||||
|
||||
### Out of Scope (Won't Fix)
|
||||
|
||||
The following are considered out of scope or accepted risks:
|
||||
|
||||
#### Self-XSS / Self-Injection
|
||||
Trilium is a personal knowledge base where users have full control over their own data. Users can intentionally create notes containing scripts, HTML, or other executable content. This is by design - Trilium's scripting system allows users to extend functionality with custom JavaScript.
|
||||
|
||||
Vulnerabilities that require a user to inject malicious content into their own notes and then view it themselves are not considered security issues.
|
||||
|
||||
#### Electron Architecture (nodeIntegration)
|
||||
Trilium's desktop application runs with `nodeIntegration: true` to enable its powerful scripting features. This is an intentional design decision, similar to VS Code extensions having full system access. We mitigate risks by:
|
||||
- Sanitizing content at input boundaries
|
||||
- Fixing specific XSS vectors as they're discovered
|
||||
- Using Electron fuses to prevent external abuse
|
||||
|
||||
#### Authenticated User Actions
|
||||
Actions that require valid authentication and only affect the authenticated user's own data are generally not vulnerabilities.
|
||||
|
||||
#### Denial of Service via Resource Exhaustion
|
||||
Creating extremely large notes or performing many operations is expected user behavior in a note-taking application.
|
||||
|
||||
#### Missing Security Headers on Non-Sensitive Endpoints
|
||||
We implement security headers where they provide meaningful protection, but may omit them on endpoints where they provide no practical benefit.
|
||||
|
||||
## Coordinated Disclosure
|
||||
|
||||
We follow a coordinated disclosure process:
|
||||
|
||||
1. **Report received** - We acknowledge receipt and begin triage
|
||||
2. **Fix developed** - We develop and test a fix privately
|
||||
3. **Release prepared** - Security release is prepared with vague changelog
|
||||
4. **Users notified** - Release is published, users encouraged to upgrade
|
||||
5. **Advisory published** - After reasonable upgrade window (typically 2-4 weeks), full advisory is published
|
||||
|
||||
We appreciate reporters allowing us time to fix issues before public disclosure. We aim to credit all reporters in published advisories unless they prefer to remain anonymous.
|
||||
|
||||
## Security Updates
|
||||
|
||||
Security fixes are released as patch versions (e.g., 0.92.1 → 0.92.2) to minimize upgrade friction. We recommend all users keep their installations up to date.
|
||||
|
||||
Subscribe to GitHub releases or watch the repository to receive notifications of new releases.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/client",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-only",
|
||||
|
||||
30
apps/client/src/services/doc_renderer.spec.ts
Normal file
30
apps/client/src/services/doc_renderer.spec.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { isValidDocName } from "./doc_renderer.js";
|
||||
|
||||
describe("isValidDocName", () => {
|
||||
it("accepts valid docNames", () => {
|
||||
expect(isValidDocName("launchbar_intro")).toBe(true);
|
||||
expect(isValidDocName("User Guide/Quick Start")).toBe(true);
|
||||
expect(isValidDocName("User Guide/User Guide/Quick Start")).toBe(true);
|
||||
expect(isValidDocName("Quick Start Guide")).toBe(true);
|
||||
expect(isValidDocName("quick_start_guide")).toBe(true);
|
||||
expect(isValidDocName("quick-start-guide")).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects path traversal attacks", () => {
|
||||
expect(isValidDocName("..")).toBe(false);
|
||||
expect(isValidDocName("../etc/passwd")).toBe(false);
|
||||
expect(isValidDocName("foo/../bar")).toBe(false);
|
||||
expect(isValidDocName("../../../../api/notes/_malicious/open")).toBe(false);
|
||||
expect(isValidDocName("..\\etc\\passwd")).toBe(false);
|
||||
expect(isValidDocName("foo\\bar")).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects URL manipulation attacks", () => {
|
||||
expect(isValidDocName("../../../../api/notes/_malicious/open?x=")).toBe(false);
|
||||
expect(isValidDocName("foo#bar")).toBe(false);
|
||||
expect(isValidDocName("%2e%2e")).toBe(false);
|
||||
expect(isValidDocName("%2e%2e%2f%2e%2e%2fapi")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -3,22 +3,39 @@ import { applyReferenceLinks } from "../widgets/type_widgets/text/read_only_help
|
||||
import { getCurrentLanguage } from "./i18n.js";
|
||||
import { formatCodeBlocks } from "./syntax_highlight.js";
|
||||
|
||||
/**
|
||||
* Validates a docName to prevent path traversal attacks.
|
||||
* Allows forward slashes for subdirectories (e.g., "User Guide/Quick Start")
|
||||
* but blocks traversal sequences and URL manipulation characters.
|
||||
*/
|
||||
export function isValidDocName(docName: string): boolean {
|
||||
// Allow alphanumeric characters, spaces, underscores, hyphens, and forward slashes.
|
||||
const validDocNameRegex = /^[a-zA-Z0-9_/\- ]+$/;
|
||||
return validDocNameRegex.test(docName);
|
||||
}
|
||||
|
||||
export default function renderDoc(note: FNote) {
|
||||
return new Promise<JQuery<HTMLElement>>((resolve) => {
|
||||
let docName = note.getLabelValue("docName");
|
||||
const docName = note.getLabelValue("docName");
|
||||
const $content = $("<div>");
|
||||
|
||||
if (docName) {
|
||||
// find doc based on language
|
||||
const url = getUrl(docName, getCurrentLanguage());
|
||||
// find doc based on language
|
||||
const url = getUrl(docName, getCurrentLanguage());
|
||||
|
||||
if (url) {
|
||||
$content.load(url, async (response, status) => {
|
||||
// fallback to english doc if no translation available
|
||||
if (status === "error") {
|
||||
const fallbackUrl = getUrl(docName, "en");
|
||||
$content.load(fallbackUrl, async () => {
|
||||
await processContent(fallbackUrl, $content)
|
||||
|
||||
if (fallbackUrl) {
|
||||
$content.load(fallbackUrl, async () => {
|
||||
await processContent(fallbackUrl, $content);
|
||||
resolve($content);
|
||||
});
|
||||
} else {
|
||||
resolve($content);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,8 +45,6 @@ export default function renderDoc(note: FNote) {
|
||||
} else {
|
||||
resolve($content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,7 +54,7 @@ async function processContent(url: string, $content: JQuery<HTMLElement>) {
|
||||
// Images are relative to the docnote but that will not work when rendered in the application since the path breaks.
|
||||
$content.find("img").each((i, el) => {
|
||||
const $img = $(el);
|
||||
$img.attr("src", dir + "/" + $img.attr("src"));
|
||||
$img.attr("src", `${dir}/${$img.attr("src")}`);
|
||||
});
|
||||
|
||||
formatCodeBlocks($content);
|
||||
@@ -48,10 +63,17 @@ async function processContent(url: string, $content: JQuery<HTMLElement>) {
|
||||
await applyReferenceLinks($content[0]);
|
||||
}
|
||||
|
||||
function getUrl(docNameValue: string, language: string) {
|
||||
function getUrl(docNameValue: string | null, language: string) {
|
||||
if (!docNameValue) return;
|
||||
|
||||
if (!isValidDocName(docNameValue)) {
|
||||
console.error(`Invalid docName: ${docNameValue}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Cannot have spaces in the URL due to how JQuery.load works.
|
||||
docNameValue = docNameValue.replaceAll(" ", "%20");
|
||||
|
||||
const basePath = window.glob.isDev ? window.glob.assetPath + "/.." : window.glob.assetPath;
|
||||
const basePath = window.glob.isDev ? `${window.glob.assetPath }/..` : window.glob.assetPath;
|
||||
return `${basePath}/doc_notes/${language}/${docNameValue}.html`;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useEffect } from "preact/hooks";
|
||||
|
||||
import { removeToastFromStore, ToastOptionsWithRequiredId, toasts } from "../services/toast";
|
||||
import Icon from "./react/Icon";
|
||||
import { RawHtmlBlock } from "./react/RawHtml";
|
||||
import Button from "./react/Button";
|
||||
|
||||
export default function ToastContainer() {
|
||||
@@ -54,7 +53,7 @@ function Toast({ id, title, timeout, progress, message, icon, buttons }: ToastOp
|
||||
<div class="toast-icon">{toastIcon}</div>
|
||||
)}
|
||||
|
||||
<RawHtmlBlock className="toast-body" html={message} />
|
||||
<div className="toast-body">{message}</div>
|
||||
|
||||
{!title && <div class="toast-header">{closeButton}</div>}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ForgeConfig } from "@electron-forge/shared-types";
|
||||
import { FuseV1Options, FuseVersion } from "@electron/fuses";
|
||||
import { LOCALES } from "@triliumnext/commons";
|
||||
import { existsSync } from "fs";
|
||||
import fs from "fs-extra";
|
||||
@@ -166,6 +167,17 @@ const config: ForgeConfig = {
|
||||
{
|
||||
name: "@electron-forge/plugin-auto-unpack-natives",
|
||||
config: {}
|
||||
},
|
||||
{
|
||||
name: "@electron-forge/plugin-fuses",
|
||||
config: {
|
||||
version: FuseVersion.V1,
|
||||
[FuseV1Options.RunAsNode]: false,
|
||||
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
|
||||
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
||||
[FuseV1Options.EnableCookieEncryption]: true,
|
||||
[FuseV1Options.OnlyLoadAppFromAsar]: true
|
||||
}
|
||||
}
|
||||
],
|
||||
hooks: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/desktop",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"description": "Build your personal knowledge base with Trilium Notes",
|
||||
"private": true,
|
||||
"main": "src/main.ts",
|
||||
@@ -27,15 +27,10 @@
|
||||
"electron-debug": "4.1.0",
|
||||
"electron-dl": "4.0.0",
|
||||
"electron-squirrel-startup": "1.0.1",
|
||||
"jquery.fancytree": "2.38.5",
|
||||
"jquery-hotkeys": "0.2.2"
|
||||
"jquery-hotkeys": "0.2.2",
|
||||
"jquery.fancytree": "2.38.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/electron-squirrel-startup": "1.0.2",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/server": "workspace:*",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"electron": "40.6.1",
|
||||
"@electron-forge/cli": "7.11.1",
|
||||
"@electron-forge/maker-deb": "7.11.1",
|
||||
"@electron-forge/maker-dmg": "7.11.1",
|
||||
@@ -44,6 +39,13 @@
|
||||
"@electron-forge/maker-squirrel": "7.11.1",
|
||||
"@electron-forge/maker-zip": "7.11.1",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.11.1",
|
||||
"@electron-forge/plugin-fuses": "7.11.1",
|
||||
"@electron/fuses": "1.8.0",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/server": "workspace:*",
|
||||
"@types/electron-squirrel-startup": "1.0.2",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"electron": "40.6.1",
|
||||
"prebuild-install": "7.1.3"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/edit-docs",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"private": true,
|
||||
"description": "Desktop version of Trilium which imports the demo database (presented to new users at start-up) or the user guide and other documentation and saves the modifications for committing.",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/server",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"description": "The server-side component of TriliumNext, which exposes the client via the web, allows for sync and provides a REST API for both internal and external use.",
|
||||
"private": true,
|
||||
"main": "./src/main.ts",
|
||||
|
||||
@@ -66,6 +66,11 @@ function register(router: Router) {
|
||||
eu.validateAndPatch(_params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_NOTE);
|
||||
const params = _params as NoteParams;
|
||||
|
||||
// Validate MIME type for image notes
|
||||
if (params.type === "image" && params.mime && !params.mime.toLowerCase().startsWith("image/")) {
|
||||
throw new eu.EtapiError(400, "INVALID_MIME_FOR_IMAGE", `MIME type '${params.mime}' is not allowed for image notes. MIME must start with 'image/'.`);
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = noteService.createNewNote(params);
|
||||
|
||||
@@ -93,6 +98,14 @@ function register(router: Router) {
|
||||
throw new eu.EtapiError(400, "NOTE_IS_PROTECTED", `Note '${req.params.noteId}' is protected and cannot be modified through ETAPI.`);
|
||||
}
|
||||
|
||||
// Validate MIME type for image notes (check both current and new type/mime)
|
||||
const effectiveType = req.body.type ?? note.type;
|
||||
const effectiveMime = req.body.mime ?? note.mime;
|
||||
const normalizedEffectiveMime = typeof effectiveMime === "string" ? effectiveMime.toLowerCase() : effectiveMime;
|
||||
if (effectiveType === "image" && normalizedEffectiveMime && !normalizedEffectiveMime.startsWith("image/")) {
|
||||
throw new eu.EtapiError(400, "INVALID_MIME_FOR_IMAGE", `MIME type '${effectiveMime}' is not allowed for image notes. MIME must start with 'image/'.`);
|
||||
}
|
||||
|
||||
noteService.saveRevisionIfNeeded(note);
|
||||
eu.validateAndPatch(note, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
|
||||
note.save();
|
||||
|
||||
@@ -232,6 +232,10 @@ function uploadModifiedFileToAttachment(req: Request) {
|
||||
const { attachmentId } = req.params;
|
||||
const { filePath } = req.body;
|
||||
|
||||
if (!createdTemporaryFiles.has(filePath)) {
|
||||
throw new ValidationError(`File '${filePath}' is not a temporary file.`);
|
||||
}
|
||||
|
||||
const attachment = becca.getAttachmentOrThrow(attachmentId);
|
||||
|
||||
log.info(`Updating attachment '${attachmentId}' with content from '${filePath}'`);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
import imageService from "../../services/image.js";
|
||||
import becca from "../../becca/becca.js";
|
||||
import fs from "fs";
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import fs from "fs";
|
||||
|
||||
import becca from "../../becca/becca.js";
|
||||
import type BNote from "../../becca/entities/bnote.js";
|
||||
import type BRevision from "../../becca/entities/brevision.js";
|
||||
import imageService from "../../services/image.js";
|
||||
import { RESOURCE_DIR } from "../../services/resource_dir.js";
|
||||
import { sanitizeSvg } from "../../services/utils.js";
|
||||
|
||||
function returnImageFromNote(req: Request, res: Response) {
|
||||
const image = becca.getNote(req.params.noteId);
|
||||
@@ -37,28 +39,33 @@ function returnImageInt(image: BNote | BRevision | null, res: Response) {
|
||||
} else {
|
||||
res.set("Content-Type", image.mime);
|
||||
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
res.send(image.getContent());
|
||||
|
||||
if (image.mime === "image/svg+xml") {
|
||||
sendSanitizedSvg(res, image.getContent());
|
||||
} else {
|
||||
res.send(image.getContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function renderSvgAttachment(image: BNote | BRevision, res: Response, attachmentName: string) {
|
||||
let svg: string | Buffer = `<svg xmlns="http://www.w3.org/2000/svg"></svg>`;
|
||||
let svgContent: string | Buffer = `<svg xmlns="http://www.w3.org/2000/svg"></svg>`;
|
||||
const attachment = image.getAttachmentByTitle(attachmentName);
|
||||
|
||||
if (attachment) {
|
||||
svg = attachment.getContent();
|
||||
svgContent = attachment.getContent();
|
||||
} else {
|
||||
// backwards compatibility, before attachments, the SVG was stored in the main note content as a separate key
|
||||
const contentSvg = image.getJsonContentSafely()?.svg;
|
||||
|
||||
if (contentSvg) {
|
||||
svg = contentSvg;
|
||||
svgContent = contentSvg;
|
||||
}
|
||||
}
|
||||
|
||||
res.set("Content-Type", "image/svg+xml");
|
||||
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
res.send(svg);
|
||||
sendSanitizedSvg(res, svgContent);
|
||||
}
|
||||
|
||||
function returnAttachedImage(req: Request, res: Response) {
|
||||
@@ -75,7 +82,12 @@ function returnAttachedImage(req: Request, res: Response) {
|
||||
|
||||
res.set("Content-Type", attachment.mime);
|
||||
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
res.send(attachment.getContent());
|
||||
|
||||
if (attachment.mime === "image/svg+xml") {
|
||||
sendSanitizedSvg(res, attachment.getContent());
|
||||
} else {
|
||||
res.send(attachment.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
function updateImage(req: Request) {
|
||||
@@ -116,3 +128,9 @@ export default {
|
||||
returnAttachedImage,
|
||||
updateImage
|
||||
};
|
||||
|
||||
function sendSanitizedSvg(res: Response, content: string | Buffer) {
|
||||
const svgString = typeof content === "string" ? content : content.toString("utf-8");
|
||||
res.set("Content-Security-Policy", "script-src 'none'");
|
||||
res.send(sanitizeSvg(svgString));
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ async function importZip(taskContext: TaskContext<"importNotes">, fileBuffer: Bu
|
||||
return "empty_note_id";
|
||||
}
|
||||
|
||||
if (origNoteId === "root" || origNoteId.startsWith("_") || opts?.preserveIds) {
|
||||
// these "named" noteIds don't differ between Trilium instances
|
||||
if (origNoteId === "root" || opts?.preserveIds) {
|
||||
return origNoteId;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import openIDEncryption from "./encryption/open_id_encryption.js";
|
||||
import sqlInit from "./sql_init.js";
|
||||
import options from "./options.js";
|
||||
import type { Session } from "express-openid-connect";
|
||||
import sql from "./sql.js";
|
||||
import config from "./config.js";
|
||||
|
||||
import config from "./config.js";
|
||||
import openIDEncryption from "./encryption/open_id_encryption.js";
|
||||
import options from "./options.js";
|
||||
import sql from "./sql.js";
|
||||
import sqlInit from "./sql_init.js";
|
||||
|
||||
function checkOpenIDConfig() {
|
||||
const missingVars: string[] = []
|
||||
const missingVars: string[] = [];
|
||||
if (config.MultiFactorAuthentication.oauthBaseUrl === "") {
|
||||
missingVars.push("oauthBaseUrl");
|
||||
}
|
||||
@@ -27,7 +27,7 @@ function isOpenIDEnabled() {
|
||||
|
||||
function isUserSaved() {
|
||||
const data = sql.getValue<string>("SELECT isSetup FROM user_data;");
|
||||
return data === "true" ? true : false;
|
||||
return data === "true";
|
||||
}
|
||||
|
||||
function getUsername() {
|
||||
@@ -59,34 +59,31 @@ function getOAuthStatus() {
|
||||
};
|
||||
}
|
||||
|
||||
function isTokenValid(req: Request, res: Response, next: NextFunction) {
|
||||
async function isTokenValid(req: Request, res: Response, next: NextFunction) {
|
||||
const userStatus = openIDEncryption.isSubjectIdentifierSaved();
|
||||
|
||||
if (req.oidc !== undefined) {
|
||||
const result = req.oidc
|
||||
.fetchUserInfo()
|
||||
.then((result) => {
|
||||
return {
|
||||
success: true,
|
||||
message: "Token is valid",
|
||||
user: userStatus,
|
||||
};
|
||||
})
|
||||
.catch((result) => {
|
||||
return {
|
||||
success: false,
|
||||
message: "Token is not valid",
|
||||
user: userStatus,
|
||||
};
|
||||
});
|
||||
return result;
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
message: "Token not set up",
|
||||
user: userStatus,
|
||||
};
|
||||
try {
|
||||
await req.oidc.fetchUserInfo();
|
||||
return {
|
||||
success: true,
|
||||
message: "Token is valid",
|
||||
user: userStatus,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
success: false,
|
||||
message: "Token is not valid",
|
||||
user: userStatus,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: "Token not set up",
|
||||
user: userStatus,
|
||||
};
|
||||
}
|
||||
|
||||
function getSSOIssuerName() {
|
||||
@@ -121,11 +118,10 @@ function generateOAuthConfig() {
|
||||
scope: "openid profile email",
|
||||
access_type: "offline",
|
||||
prompt: "consent",
|
||||
state: "random_state_" + Math.random().toString(36).substring(2)
|
||||
},
|
||||
routes: authRoutes,
|
||||
idpLogout: true,
|
||||
logoutParams: logoutParams,
|
||||
logoutParams,
|
||||
afterCallback: async (req: Request, res: Response, session: Session) => {
|
||||
if (!sqlInit.isDbInitialized()) return session;
|
||||
|
||||
|
||||
@@ -705,3 +705,110 @@ describe("#slugify", () => {
|
||||
expect(result).toBe(expectedSlug);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#sanitizeSvg", () => {
|
||||
it("should remove script elements", () => {
|
||||
const maliciousSvg = '<svg><script>alert("XSS")</script><rect width="100" height="100"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100" height="100"/></svg>');
|
||||
});
|
||||
|
||||
it("should remove script elements with attributes", () => {
|
||||
const maliciousSvg = '<svg><script type="text/javascript">alert("XSS")</script></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg></svg>');
|
||||
});
|
||||
|
||||
it("should remove multiline script elements", () => {
|
||||
const maliciousSvg = `<svg><script>
|
||||
var x = 1;
|
||||
alert(x);
|
||||
</script></svg>`;
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg></svg>');
|
||||
});
|
||||
|
||||
it("should remove onclick event handlers with double quotes", () => {
|
||||
const maliciousSvg = '<svg><rect onclick="doEvil()" width="100"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/></svg>');
|
||||
});
|
||||
|
||||
it("should remove onclick event handlers with single quotes", () => {
|
||||
const maliciousSvg = "<svg><rect onclick='doEvil()' width=\"100\"/></svg>";
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/></svg>');
|
||||
});
|
||||
|
||||
it("should remove onload event handlers", () => {
|
||||
const maliciousSvg = '<svg onload="doEvil()"><rect width="100"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/></svg>');
|
||||
});
|
||||
|
||||
it("should remove onerror event handlers", () => {
|
||||
const maliciousSvg = '<svg><image onerror="alert(1)" href="invalid.jpg"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><image href="invalid.jpg"/></svg>');
|
||||
});
|
||||
|
||||
it("should remove onmouseover event handlers", () => {
|
||||
const maliciousSvg = '<svg><rect onmouseover="alert(1)" width="100"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/></svg>');
|
||||
});
|
||||
|
||||
it("should remove event handlers without quotes", () => {
|
||||
const maliciousSvg = '<svg><rect onclick=alert(1) width="100"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/></svg>');
|
||||
});
|
||||
|
||||
it("should replace javascript: URLs in href with #", () => {
|
||||
const maliciousSvg = '<svg><a href="javascript:alert(1)"><text>Click me</text></a></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><a href="#"><text>Click me</text></a></svg>');
|
||||
});
|
||||
|
||||
it("should replace javascript: URLs in xlink:href with #", () => {
|
||||
const maliciousSvg = '<svg><a xlink:href="javascript:alert(1)"><text>Click me</text></a></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><a xlink:href="#"><text>Click me</text></a></svg>');
|
||||
});
|
||||
|
||||
it("should preserve valid SVG content", () => {
|
||||
const validSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect x="10" y="10" width="80" height="80" fill="blue"/><circle cx="50" cy="50" r="30" fill="red"/></svg>';
|
||||
const result = utils.sanitizeSvg(validSvg);
|
||||
expect(result).toBe(validSvg);
|
||||
});
|
||||
|
||||
it("should preserve valid href URLs", () => {
|
||||
const validSvg = '<svg><a href="https://example.com"><text>Link</text></a></svg>';
|
||||
const result = utils.sanitizeSvg(validSvg);
|
||||
expect(result).toBe(validSvg);
|
||||
});
|
||||
|
||||
it("should handle multiple malicious elements", () => {
|
||||
const maliciousSvg = '<svg onload="evil()"><script>evil()</script><rect onclick="bad()" width="100"/><a href="javascript:attack()">link</a></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/><a href="#">link</a></svg>');
|
||||
});
|
||||
|
||||
it("should handle empty SVG", () => {
|
||||
const emptySvg = '<svg></svg>';
|
||||
const result = utils.sanitizeSvg(emptySvg);
|
||||
expect(result).toBe('<svg></svg>');
|
||||
});
|
||||
|
||||
it("should be case insensitive for script tags", () => {
|
||||
const maliciousSvg = '<svg><SCRIPT>alert(1)</SCRIPT><Script>alert(2)</Script></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg></svg>');
|
||||
});
|
||||
|
||||
it("should be case insensitive for event handlers", () => {
|
||||
const maliciousSvg = '<svg><rect ONCLICK="alert(1)" width="100"/></svg>';
|
||||
const result = utils.sanitizeSvg(maliciousSvg);
|
||||
expect(result).toBe('<svg><rect width="100"/></svg>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,6 +119,22 @@ export function sanitizeSqlIdentifier(str: string) {
|
||||
return str.replace(/[^A-Za-z0-9_]/g, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize SVG to remove potentially dangerous elements and attributes.
|
||||
* This prevents XSS via script injection in SVG content.
|
||||
*/
|
||||
export function sanitizeSvg(svg: string): string {
|
||||
return svg
|
||||
// Remove script elements
|
||||
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
||||
// Remove on* event handlers (onclick, onload, onerror, etc.)
|
||||
.replace(/\s+on\w+\s*=\s*["'][^"']*["']/gi, '')
|
||||
.replace(/\s+on\w+\s*=\s*[^\s>]+/gi, '')
|
||||
// Remove javascript: URLs
|
||||
.replace(/href\s*=\s*["']javascript:[^"']*["']/gi, 'href="#"')
|
||||
.replace(/xlink:href\s*=\s*["']javascript:[^"']*["']/gi, 'xlink:href="#"');
|
||||
}
|
||||
|
||||
export const escapeHtml = escape;
|
||||
|
||||
export const unescapeHtml = unescape;
|
||||
@@ -556,6 +572,7 @@ export default {
|
||||
replaceAll,
|
||||
safeExtractMessageAndStackFromError,
|
||||
sanitizeSqlIdentifier,
|
||||
sanitizeSvg,
|
||||
stripTags,
|
||||
slugify,
|
||||
timeLimit,
|
||||
|
||||
@@ -9,7 +9,7 @@ import SearchContext from "../services/search/search_context.js";
|
||||
import type SNote from "./shaca/entities/snote.js";
|
||||
import type SAttachment from "./shaca/entities/sattachment.js";
|
||||
import { getDefaultTemplatePath, renderNoteContent } from "./content_renderer.js";
|
||||
import utils from "../services/utils.js";
|
||||
import utils, { sanitizeSvg } from "../services/utils.js";
|
||||
|
||||
function addNoIndexHeader(note: SNote, res: Response) {
|
||||
if (note.isLabelTruthy("shareDisallowRobotIndexing")) {
|
||||
@@ -102,9 +102,10 @@ function renderImageAttachment(image: SNote, res: Response, attachmentName: stri
|
||||
}
|
||||
}
|
||||
|
||||
const svg = svgString;
|
||||
const svg = sanitizeSvg(svgString);
|
||||
res.set("Content-Type", "image/svg+xml");
|
||||
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
res.set("Content-Security-Policy", "script-src 'none'");
|
||||
res.send(svg);
|
||||
}
|
||||
|
||||
|
||||
132
docs/Release Notes/!!!meta.json
vendored
132
docs/Release Notes/!!!meta.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"formatVersion": 2,
|
||||
"appVersion": "0.102.0",
|
||||
"appVersion": "0.102.1",
|
||||
"files": [
|
||||
{
|
||||
"isClone": false,
|
||||
@@ -61,6 +61,32 @@
|
||||
"attachments": [],
|
||||
"dirFileName": "Release Notes",
|
||||
"children": [
|
||||
{
|
||||
"isClone": false,
|
||||
"noteId": "ZdWJsMQvY1fo",
|
||||
"notePath": [
|
||||
"hD3V4hiu2VW4",
|
||||
"ZdWJsMQvY1fo"
|
||||
],
|
||||
"title": "v0.102.2",
|
||||
"notePosition": 10,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
"mime": "text/html",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "template",
|
||||
"value": "wyurrlcDl416",
|
||||
"isInheritable": false,
|
||||
"position": 60
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
"dataFileName": "v0.102.2.md",
|
||||
"attachments": []
|
||||
},
|
||||
{
|
||||
"isClone": false,
|
||||
"noteId": "4FTGCuCiG7s7",
|
||||
@@ -69,7 +95,7 @@
|
||||
"4FTGCuCiG7s7"
|
||||
],
|
||||
"title": "v0.102.1",
|
||||
"notePosition": 10,
|
||||
"notePosition": 20,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -95,7 +121,7 @@
|
||||
"d582eD4RY4OM"
|
||||
],
|
||||
"title": "v0.102.0",
|
||||
"notePosition": 20,
|
||||
"notePosition": 30,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -121,7 +147,7 @@
|
||||
"IlBzLeN3MJhw"
|
||||
],
|
||||
"title": "v0.101.3",
|
||||
"notePosition": 30,
|
||||
"notePosition": 40,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -147,7 +173,7 @@
|
||||
"vcBthaXcwAm6"
|
||||
],
|
||||
"title": "v0.101.2",
|
||||
"notePosition": 40,
|
||||
"notePosition": 50,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -173,7 +199,7 @@
|
||||
"AgUcrU9nFXuW"
|
||||
],
|
||||
"title": "v0.101.1",
|
||||
"notePosition": 50,
|
||||
"notePosition": 60,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -199,7 +225,7 @@
|
||||
"uYwlZ594eyJu"
|
||||
],
|
||||
"title": "v0.101.0",
|
||||
"notePosition": 60,
|
||||
"notePosition": 70,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -225,7 +251,7 @@
|
||||
"iPGKEk7pwJXK"
|
||||
],
|
||||
"title": "v0.100.0",
|
||||
"notePosition": 70,
|
||||
"notePosition": 80,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -251,7 +277,7 @@
|
||||
"7HKMTjmopLcM"
|
||||
],
|
||||
"title": "v0.99.5",
|
||||
"notePosition": 80,
|
||||
"notePosition": 90,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -277,7 +303,7 @@
|
||||
"RMBaNYPsRpIr"
|
||||
],
|
||||
"title": "v0.99.4",
|
||||
"notePosition": 90,
|
||||
"notePosition": 100,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -303,7 +329,7 @@
|
||||
"yuroLztFfpu5"
|
||||
],
|
||||
"title": "v0.99.3",
|
||||
"notePosition": 100,
|
||||
"notePosition": 110,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -329,7 +355,7 @@
|
||||
"z207sehwMJ6C"
|
||||
],
|
||||
"title": "v0.99.2",
|
||||
"notePosition": 110,
|
||||
"notePosition": 120,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -355,7 +381,7 @@
|
||||
"WGQsXq2jNyTi"
|
||||
],
|
||||
"title": "v0.99.1",
|
||||
"notePosition": 120,
|
||||
"notePosition": 130,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -381,7 +407,7 @@
|
||||
"cyw2Yue9vXf3"
|
||||
],
|
||||
"title": "v0.99.0",
|
||||
"notePosition": 130,
|
||||
"notePosition": 140,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -407,7 +433,7 @@
|
||||
"QOJwjruOUr4k"
|
||||
],
|
||||
"title": "v0.98.1",
|
||||
"notePosition": 140,
|
||||
"notePosition": 150,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -433,7 +459,7 @@
|
||||
"PLUoryywi0BC"
|
||||
],
|
||||
"title": "v0.98.0",
|
||||
"notePosition": 150,
|
||||
"notePosition": 160,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -459,7 +485,7 @@
|
||||
"lvOuiWsLDv8F"
|
||||
],
|
||||
"title": "v0.97.2",
|
||||
"notePosition": 160,
|
||||
"notePosition": 170,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -485,7 +511,7 @@
|
||||
"OtFZ6Nd9vM3n"
|
||||
],
|
||||
"title": "v0.97.1",
|
||||
"notePosition": 170,
|
||||
"notePosition": 180,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -511,7 +537,7 @@
|
||||
"SJZ5PwfzHSQ1"
|
||||
],
|
||||
"title": "v0.97.0",
|
||||
"notePosition": 180,
|
||||
"notePosition": 190,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -537,7 +563,7 @@
|
||||
"mYXFde3LuNR7"
|
||||
],
|
||||
"title": "v0.96.0",
|
||||
"notePosition": 190,
|
||||
"notePosition": 200,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -563,7 +589,7 @@
|
||||
"jthwbL0FdaeU"
|
||||
],
|
||||
"title": "v0.95.0",
|
||||
"notePosition": 200,
|
||||
"notePosition": 210,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -589,7 +615,7 @@
|
||||
"7HGYsJbLuhnv"
|
||||
],
|
||||
"title": "v0.94.1",
|
||||
"notePosition": 210,
|
||||
"notePosition": 220,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -615,7 +641,7 @@
|
||||
"Neq53ujRGBqv"
|
||||
],
|
||||
"title": "v0.94.0",
|
||||
"notePosition": 220,
|
||||
"notePosition": 230,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -641,7 +667,7 @@
|
||||
"VN3xnce1vLkX"
|
||||
],
|
||||
"title": "v0.93.0",
|
||||
"notePosition": 230,
|
||||
"notePosition": 240,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -659,7 +685,7 @@
|
||||
"WRaBfQqPr6qo"
|
||||
],
|
||||
"title": "v0.92.7",
|
||||
"notePosition": 240,
|
||||
"notePosition": 250,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -685,7 +711,7 @@
|
||||
"a2rwfKNmUFU1"
|
||||
],
|
||||
"title": "v0.92.6",
|
||||
"notePosition": 250,
|
||||
"notePosition": 260,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -703,7 +729,7 @@
|
||||
"fEJ8qErr0BKL"
|
||||
],
|
||||
"title": "v0.92.5-beta",
|
||||
"notePosition": 260,
|
||||
"notePosition": 270,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -721,7 +747,7 @@
|
||||
"kkkZQQGSXjwy"
|
||||
],
|
||||
"title": "v0.92.4",
|
||||
"notePosition": 270,
|
||||
"notePosition": 280,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -739,7 +765,7 @@
|
||||
"vAroNixiezaH"
|
||||
],
|
||||
"title": "v0.92.3-beta",
|
||||
"notePosition": 280,
|
||||
"notePosition": 290,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -757,7 +783,7 @@
|
||||
"mHEq1wxAKNZd"
|
||||
],
|
||||
"title": "v0.92.2-beta",
|
||||
"notePosition": 290,
|
||||
"notePosition": 300,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -775,7 +801,7 @@
|
||||
"IykjoAmBpc61"
|
||||
],
|
||||
"title": "v0.92.1-beta",
|
||||
"notePosition": 300,
|
||||
"notePosition": 310,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -793,7 +819,7 @@
|
||||
"dq2AJ9vSBX4Y"
|
||||
],
|
||||
"title": "v0.92.0-beta",
|
||||
"notePosition": 310,
|
||||
"notePosition": 320,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -811,7 +837,7 @@
|
||||
"3a8aMe4jz4yM"
|
||||
],
|
||||
"title": "v0.91.6",
|
||||
"notePosition": 320,
|
||||
"notePosition": 330,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -829,7 +855,7 @@
|
||||
"8djQjkiDGESe"
|
||||
],
|
||||
"title": "v0.91.5",
|
||||
"notePosition": 330,
|
||||
"notePosition": 340,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -847,7 +873,7 @@
|
||||
"OylxVoVJqNmr"
|
||||
],
|
||||
"title": "v0.91.4-beta",
|
||||
"notePosition": 340,
|
||||
"notePosition": 350,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -865,7 +891,7 @@
|
||||
"tANGQDvnyhrj"
|
||||
],
|
||||
"title": "v0.91.3-beta",
|
||||
"notePosition": 350,
|
||||
"notePosition": 360,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -883,7 +909,7 @@
|
||||
"hMoBfwSoj1SC"
|
||||
],
|
||||
"title": "v0.91.2-beta",
|
||||
"notePosition": 360,
|
||||
"notePosition": 370,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -901,7 +927,7 @@
|
||||
"a2XMSKROCl9z"
|
||||
],
|
||||
"title": "v0.91.1-beta",
|
||||
"notePosition": 370,
|
||||
"notePosition": 380,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -919,7 +945,7 @@
|
||||
"yqXFvWbLkuMD"
|
||||
],
|
||||
"title": "v0.90.12",
|
||||
"notePosition": 380,
|
||||
"notePosition": 390,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -937,7 +963,7 @@
|
||||
"veS7pg311yJP"
|
||||
],
|
||||
"title": "v0.90.11-beta",
|
||||
"notePosition": 390,
|
||||
"notePosition": 400,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -955,7 +981,7 @@
|
||||
"sq5W9TQxRqMq"
|
||||
],
|
||||
"title": "v0.90.10-beta",
|
||||
"notePosition": 400,
|
||||
"notePosition": 410,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -973,7 +999,7 @@
|
||||
"yFEGVCUM9tPx"
|
||||
],
|
||||
"title": "v0.90.9-beta",
|
||||
"notePosition": 410,
|
||||
"notePosition": 420,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -991,7 +1017,7 @@
|
||||
"o4wAGqOQuJtV"
|
||||
],
|
||||
"title": "v0.90.8",
|
||||
"notePosition": 420,
|
||||
"notePosition": 430,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1024,7 +1050,7 @@
|
||||
"i4A5g9iOg9I0"
|
||||
],
|
||||
"title": "v0.90.7-beta",
|
||||
"notePosition": 430,
|
||||
"notePosition": 440,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1042,7 +1068,7 @@
|
||||
"ThNf2GaKgXUs"
|
||||
],
|
||||
"title": "v0.90.6-beta",
|
||||
"notePosition": 440,
|
||||
"notePosition": 450,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1060,7 +1086,7 @@
|
||||
"G4PAi554kQUr"
|
||||
],
|
||||
"title": "v0.90.5-beta",
|
||||
"notePosition": 450,
|
||||
"notePosition": 460,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1087,7 +1113,7 @@
|
||||
"zATRobGRCmBn"
|
||||
],
|
||||
"title": "v0.90.4",
|
||||
"notePosition": 460,
|
||||
"notePosition": 470,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1105,7 +1131,7 @@
|
||||
"sCDLf8IKn3Iz"
|
||||
],
|
||||
"title": "v0.90.3",
|
||||
"notePosition": 470,
|
||||
"notePosition": 480,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1123,7 +1149,7 @@
|
||||
"VqqyBu4AuTjC"
|
||||
],
|
||||
"title": "v0.90.2-beta",
|
||||
"notePosition": 480,
|
||||
"notePosition": 490,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1141,7 +1167,7 @@
|
||||
"RX3Nl7wInLsA"
|
||||
],
|
||||
"title": "v0.90.1-beta",
|
||||
"notePosition": 490,
|
||||
"notePosition": 500,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1159,7 +1185,7 @@
|
||||
"GyueACukPWjk"
|
||||
],
|
||||
"title": "v0.90.0-beta",
|
||||
"notePosition": 500,
|
||||
"notePosition": 510,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1177,7 +1203,7 @@
|
||||
"kzjHexDTTeVB"
|
||||
],
|
||||
"title": "v0.48",
|
||||
"notePosition": 510,
|
||||
"notePosition": 520,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
@@ -1244,7 +1270,7 @@
|
||||
"wyurrlcDl416"
|
||||
],
|
||||
"title": "Release Template",
|
||||
"notePosition": 520,
|
||||
"notePosition": 530,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
|
||||
@@ -32,4 +32,8 @@
|
||||
|
||||
## 🛠️ Technical updates
|
||||
|
||||
* \[…\]
|
||||
|
||||
## 🔒️ Security improvements
|
||||
|
||||
* \[…\]
|
||||
37
docs/Release Notes/Release Notes/v0.102.2.md
vendored
Normal file
37
docs/Release Notes/Release Notes/v0.102.2.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# v0.102.2
|
||||
> [!IMPORTANT]
|
||||
> **This release contains important security fixes. All users are strongly encouraged to update immediately.**
|
||||
>
|
||||
> Several vulnerabilities affecting content handling and the desktop application have been addressed. We recommend upgrading before the next scheduled release to ensure your installation is protected.
|
||||
|
||||
> [!NOTE]
|
||||
> If you enjoyed this release, consider showing a token of appreciation by:
|
||||
>
|
||||
> * Pressing the “Star” button on [GitHub](https://github.com/TriliumNext/Trilium) (top-right).
|
||||
> * Considering a one-time or recurrent donation to the [lead developer](https://github.com/eliandoran) via [GitHub Sponsors](https://github.com/sponsors/eliandoran) or [PayPal](https://paypal.me/eliandoran).
|
||||
> * If you are interested in an [official mobile application](https://oss.issuehunt.io/r/TriliumNext/Trilium/issues/7447) ([#7447](https://github.com/TriliumNext/Trilium/issues/7447)) or [multi-user support](https://oss.issuehunt.io/r/TriliumNext/Trilium/issues/4956) ([#4956](https://github.com/TriliumNext/Trilium/issues/4956)), consider offering financial support via IssueHunt (see links).
|
||||
|
||||
## 🔒️ Security improvements
|
||||
|
||||
* Content Handling
|
||||
|
||||
* Improved request handling for SVG content in share routes
|
||||
* Improved request handling for SVG content in the main API
|
||||
* Enhanced content rendering in the Mermaid diagram editor
|
||||
* Fixed toast notifications to properly escape content
|
||||
* Added validation for the `docName` attribute in the document renderer
|
||||
* Marked `docName` as a sensitive attribute in the commons module
|
||||
* Desktop Application (Electron)
|
||||
|
||||
* Added Electron fuses to harden the desktop application against external abuse
|
||||
* Improved application integrity checks
|
||||
* API & Import
|
||||
|
||||
* Added MIME type validation for image uploads via ETAPI
|
||||
* Aligned attachment upload validation with note upload validation
|
||||
* Import no longer preserves named note IDs to prevent potential conflicts
|
||||
* Authentication
|
||||
|
||||
* OpenID Connect now uses a more secure random number generator
|
||||
|
||||
We've also updated our SECURITY.MD file to detail our security practices and how to report vulnerabilities.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/source",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"description": "Build your personal knowledge base with Trilium Notes",
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
|
||||
@@ -183,7 +183,7 @@ export default class MermaidEditing extends Plugin {
|
||||
const mermaidSource = data.item.getAttribute( 'source' ) as string;
|
||||
const domElement = this.toDomElement( domDocument );
|
||||
|
||||
domElement.innerHTML = mermaidSource;
|
||||
domElement.textContent = mermaidSource;
|
||||
|
||||
window.setTimeout( () => {
|
||||
// @todo: by the looks of it the domElement needs to be hooked to tree in order to allow for rendering.
|
||||
@@ -219,7 +219,7 @@ export default class MermaidEditing extends Plugin {
|
||||
const domPreviewWrapper = domConverter.viewToDom(child);
|
||||
|
||||
if ( domPreviewWrapper ) {
|
||||
domPreviewWrapper.innerHTML = newSource;
|
||||
domPreviewWrapper.textContent = newSource;
|
||||
domPreviewWrapper.removeAttribute( 'data-processed' );
|
||||
|
||||
this._renderMermaid( domPreviewWrapper );
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/commons",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -81,6 +81,7 @@ export default [
|
||||
{ type: "label", name: "webViewSrc", isDangerous: true },
|
||||
{ type: "label", name: "hideHighlightWidget" },
|
||||
{ type: "label", name: "iconPack", isDangerous: true },
|
||||
{ type: "label", name: "docName", isDangerous: true },
|
||||
|
||||
{ type: "label", name: "printLandscape" },
|
||||
{ type: "label", name: "printPageSize" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/pdfjs-viewer",
|
||||
"version": "0.102.1",
|
||||
"version": "0.102.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsx scripts/build.ts",
|
||||
|
||||
85
pnpm-lock.yaml
generated
85
pnpm-lock.yaml
generated
@@ -445,6 +445,12 @@ importers:
|
||||
'@electron-forge/plugin-auto-unpack-natives':
|
||||
specifier: 7.11.1
|
||||
version: 7.11.1
|
||||
'@electron-forge/plugin-fuses':
|
||||
specifier: 7.11.1
|
||||
version: 7.11.1(@electron/fuses@1.8.0)
|
||||
'@electron/fuses':
|
||||
specifier: 1.8.0
|
||||
version: 1.8.0
|
||||
'@triliumnext/commons':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/commons
|
||||
@@ -2345,6 +2351,12 @@ packages:
|
||||
resolution: {integrity: sha512-lKpSOV1GA3FoYiD9k05i6v4KaQVmojnRgCr7d6VL1bFp13QOtXSaAWhFI9mtSY7rGElOacX6Zt7P7rPoB8T9eQ==}
|
||||
engines: {node: '>= 16.4.0'}
|
||||
|
||||
'@electron-forge/plugin-fuses@7.11.1':
|
||||
resolution: {integrity: sha512-Td517mHf+RjQAayFDM2kKb7NaGdRXrZfPbc7KOHlGbXthp5YTkFu2cCZGWokiqt1y1wsFaAodULhqBIg7vbbbw==}
|
||||
engines: {node: '>= 16.4.0'}
|
||||
peerDependencies:
|
||||
'@electron/fuses': ^1.0.0
|
||||
|
||||
'@electron-forge/publisher-base@7.11.1':
|
||||
resolution: {integrity: sha512-rXE9oMFGMtdQrixnumWYH5TTGsp99iPHZb3jI74YWq518ctCh6DlIgWlhf6ok2X0+lhWovcIb45KJucUFAQ13w==}
|
||||
engines: {node: '>= 16.4.0'}
|
||||
@@ -2382,6 +2394,10 @@ packages:
|
||||
engines: {node: '>=10.12.0'}
|
||||
hasBin: true
|
||||
|
||||
'@electron/fuses@1.8.0':
|
||||
resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==}
|
||||
hasBin: true
|
||||
|
||||
'@electron/get@2.0.3':
|
||||
resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -16058,6 +16074,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.4.0
|
||||
'@ckeditor/ckeditor5-upload': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-ai@47.4.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
|
||||
dependencies:
|
||||
@@ -16198,12 +16216,16 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-cloud-services@47.4.0':
|
||||
dependencies:
|
||||
'@ckeditor/ckeditor5-core': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-code-block@47.4.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
||||
dependencies:
|
||||
@@ -16396,6 +16418,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-classic@47.4.0':
|
||||
dependencies:
|
||||
@@ -16405,6 +16429,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-decoupled@47.4.0':
|
||||
dependencies:
|
||||
@@ -16414,6 +16440,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-inline@47.4.0':
|
||||
dependencies:
|
||||
@@ -16447,8 +16475,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-table': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-emoji@47.4.0':
|
||||
dependencies:
|
||||
@@ -16505,8 +16531,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-export-word@47.4.0':
|
||||
dependencies:
|
||||
@@ -16531,6 +16555,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-font@47.4.0':
|
||||
dependencies:
|
||||
@@ -16633,8 +16659,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-icons@47.4.0': {}
|
||||
|
||||
@@ -16666,8 +16690,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-indent@47.4.0':
|
||||
dependencies:
|
||||
@@ -16679,8 +16701,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-inspector@5.0.0': {}
|
||||
|
||||
@@ -16690,8 +16710,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-line-height@47.4.0':
|
||||
dependencies:
|
||||
@@ -16716,8 +16734,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-list-multi-level@47.4.0':
|
||||
dependencies:
|
||||
@@ -16741,8 +16757,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-markdown-gfm@47.4.0':
|
||||
dependencies:
|
||||
@@ -16780,8 +16794,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-mention@47.4.0(patch_hash=5981fb59ba35829e4dff1d39cf771000f8a8fdfa7a34b51d8af9549541f2d62d)':
|
||||
dependencies:
|
||||
@@ -16791,8 +16803,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-merge-fields@47.4.0':
|
||||
dependencies:
|
||||
@@ -16805,8 +16815,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-minimap@47.4.0':
|
||||
dependencies:
|
||||
@@ -16815,8 +16823,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-operations-compressor@47.4.0':
|
||||
dependencies:
|
||||
@@ -16871,8 +16877,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-pagination@47.4.0':
|
||||
dependencies:
|
||||
@@ -16992,8 +16996,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-source-editing-enhanced@47.4.0':
|
||||
dependencies:
|
||||
@@ -17041,8 +17043,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-table@47.4.0':
|
||||
dependencies:
|
||||
@@ -17055,8 +17055,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-template@47.4.0':
|
||||
dependencies:
|
||||
@@ -17131,8 +17129,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-icons': 47.4.0
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-upload@47.4.0':
|
||||
dependencies:
|
||||
@@ -17169,8 +17165,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-engine': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-widget@47.4.0':
|
||||
dependencies:
|
||||
@@ -17190,8 +17184,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@codemirror/autocomplete@6.18.6':
|
||||
dependencies:
|
||||
@@ -17615,6 +17607,15 @@ snapshots:
|
||||
- bluebird
|
||||
- supports-color
|
||||
|
||||
'@electron-forge/plugin-fuses@7.11.1(@electron/fuses@1.8.0)':
|
||||
dependencies:
|
||||
'@electron-forge/plugin-base': 7.11.1
|
||||
'@electron-forge/shared-types': 7.11.1
|
||||
'@electron/fuses': 1.8.0
|
||||
transitivePeerDependencies:
|
||||
- bluebird
|
||||
- supports-color
|
||||
|
||||
'@electron-forge/publisher-base@7.11.1':
|
||||
dependencies:
|
||||
'@electron-forge/shared-types': 7.11.1
|
||||
@@ -17697,6 +17698,12 @@ snapshots:
|
||||
glob: 7.2.3
|
||||
minimatch: 3.1.2
|
||||
|
||||
'@electron/fuses@1.8.0':
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
fs-extra: 9.1.0
|
||||
minimist: 1.2.8
|
||||
|
||||
'@electron/get@2.0.3':
|
||||
dependencies:
|
||||
debug: 4.4.3(supports-color@8.1.1)
|
||||
@@ -23148,8 +23155,6 @@ snapshots:
|
||||
ckeditor5-collaboration@47.4.0:
|
||||
dependencies:
|
||||
'@ckeditor/ckeditor5-collaboration-core': 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ckeditor5-premium-features@47.4.0(bufferutil@4.0.9)(ckeditor5@47.4.0)(utf-8-validate@6.0.5):
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user