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
12 changes: 12 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM mcr.microsoft.com/devcontainers/javascript-node:4-24-trixie

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"

# [Optional] Uncomment if you want to install more global node modules
RUN su node -c "npm install -g @devcontainers/cli"
22 changes: 22 additions & 0 deletions .devcontainer/devcontainer-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"features": {
"ghcr.io/devcontainers-community/npm-features/prettier:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers-community/npm-features/prettier@sha256:41aca434d43373d78b276d3cd89ecdce960e0b618ff0ee59f2ecee0810dbabe9",
"integrity": "sha256:41aca434d43373d78b276d3cd89ecdce960e0b618ff0ee59f2ecee0810dbabe9",
"dependsOn": [
"ghcr.io/devcontainers/features/node"
]
},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
"version": "1.10.0",
"resolved": "ghcr.io/devcontainers/features/docker-outside-of-docker@sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e",
"integrity": "sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e"
},
"ghcr.io/devcontainers/features/node": {
"version": "2.1.0",
"resolved": "ghcr.io/devcontainers/features/node@sha256:586c9a6f7dd40bd3ba2cd41e7f2f88dcc31fbe5d1442afcbf07ffbc66b686857",
"integrity": "sha256:586c9a6f7dd40bd3ba2cd41e7f2f88dcc31fbe5d1442afcbf07ffbc66b686857"
}
}
}
46 changes: 46 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node-postgres
{
"name": "Node.js & PostgreSQL",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"ghcr.io/devcontainers-community/npm-features/prettier:1": {},
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
"moby": false
}
},
"mounts": [
"source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind,consistency=cached"
],

"containerEnv": {
"DATABASE_URL": "postgres://postgres:password@db:5432/visualization_tool"
},

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or with the host.
// "forwardPorts": [3000, 5432],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "yarn install",

// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-azuretools.vscode-docker",
"codeandstuff.package-json-upgrade"
]
}
}

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
55 changes: 55 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
volumes:
postgres-data:
yarn-cache:

services:
app:
build:
context: .
dockerfile: Dockerfile

volumes:
- ../..:/workspaces:cached
- yarn-cache:/home/node/.yarn-cache

environment:
YARN_CACHE_FOLDER: /home/node/.cache/yarn

# Overrides default command so things don't shut down after the process ends.
command: sleep infinity

# Use proper Docker networking instead of network_mode: service:db
# to ensure reliable DNS resolution in all environments
depends_on:
db:
condition: service_healthy
networks:
- app-network

# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)

db:
image: postgres:18-alpine
restart: unless-stopped
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
- postgres-data:/var/lib/postgresql
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: postgres
POSTGRES_DB: visualization_tool

# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)

networks:
app-network:
driver: bridge
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ You can also check the

## Unreleased

- Fixes
- Harden markdown renderer sanitization
- Maintenance
- Add dev-container support

## 6.5.0 – 2026-06-01

- Features
Expand Down
51 changes: 44 additions & 7 deletions app/components/markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
import clsx from "clsx";
import { ComponentProps } from "react";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import remarkGfm from "remark-gfm";

import classes from "@/components/markdown.module.css";
import { palette } from "@/themes/palette";

const sanitizeSchema = {
...defaultSchema,
tagNames: [
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"p",
"strong",
"em",
"ins",
"del",
"s",
"ul",
"ol",
"li",
"blockquote",
"code",
"pre",
"br",
"hr",
"a",
],
attributes: {
...defaultSchema.attributes,
a: ["href", "title"],
"*": defaultSchema.attributes?.["*"] ?? [],
},
};

const components: ComponentProps<typeof ReactMarkdown>["components"] = {
h1: ({ children, className, ...props }) => (
<h1 className={clsx(className, classes.common)} {...props}>
Expand Down Expand Up @@ -46,10 +77,11 @@ const components: ComponentProps<typeof ReactMarkdown>["components"] = {
),
a: ({ children, className, style, ...props }) => (
<a
{...props}
target="_blank"
rel="noopener noreferrer"
className={clsx(className, classes.common)}
style={{ ...style, color: palette.primary.main }}
{...props}
>
{children}
</a>
Expand All @@ -63,7 +95,7 @@ export const Markdown = (
<ReactMarkdown
components={components}
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
rehypePlugins={[[rehypeSanitize, sanitizeSchema]]}
{...props}
/>
);
Expand All @@ -81,7 +113,11 @@ export const InlineMarkdown = ({
p: ({ children }) => <>{children}</>,
strong: ({ children }) => <strong>{children}</strong>,
em: ({ children }) => <em>{children}</em>,
a: ({ children, href }) => <a href={href}>{children}</a>,
a: ({ children, href }) => {
const safeHref =
href && /^(https?|mailto|ircs?):/i.test(href) ? href : undefined;
return <a href={safeHref}>{children}</a>;
},
h1: () => null,
h2: () => null,
h3: () => null,
Expand Down Expand Up @@ -163,10 +199,11 @@ const componentsInheritFonts: ComponentProps<
),
a: ({ children, className, style, ...props }) => (
<a
{...props}
className={clsx(className, classes.common, classes.inheritFonts)}
target="_blank"
rel="noopener noreferrer"
style={{ ...style, color: palette.primary.main }}
{...props}
>
{children}
</a>
Expand All @@ -180,7 +217,7 @@ export const MarkdownInheritFonts = (
<ReactMarkdown
components={componentsInheritFonts}
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
rehypePlugins={[[rehypeSanitize, sanitizeSchema]]}
{...props}
/>
);
Expand Down
1 change: 0 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@
"react-transition-group": "^4.4.5",
"react-virtualized-auto-sizer": "^1.0.25",
"react-window": "^1.8.10",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.0",
"simple-statistics": "^7.6.0",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dev:ssl": "preconstruct dev && NODE_OPTIONS=\"--inspect --openssl-legacy-provider \" next dev --experimental-https ./app",
"dev:mitmproxy": "preconstruct dev && HTTP_PROXY=http://localhost:8080 NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE='' NO_PROXY='localhost,127.0.0.1' next ./app",
"dev:rollup": "rollup -c rollup.config.js --watch",
"db:migrate:dev": "DATABASE_URL=postgres://postgres:password@localhost:5432/visualization_tool prisma db push",
"db:migrate:dev": "DATABASE_URL=${DATABASE_URL:-postgres://postgres:password@localhost:5432/visualization_tool} prisma db push",
"db:migrate": "[ -z \"$DATABASE_URL\" ] && echo 'Bypassing database migration, DATABASE_URL not set' || prisma db push",
"build": "yarn graphql:codegen && lingui compile && rollup -c rollup.config.js && yarn storybook:build && yarn generate-json-schema && next build ./app",
"build:npm": "yarn graphql:codegen && lingui compile && BABEL_ENV=NPM_PACKAGE preconstruct build",
Expand Down
17 changes: 17 additions & 0 deletions readme/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,20 @@ yarn db:migrate:dev
> scoping the new environment variables to the preview branch you are working
> on. After merging the branch, you can delete the environment variables scoped
> to the branch.
# Dev-containers

You can use dev-containers for development if you want to have a consistent
environment.

Codeium based IDE do not support some convenience functionality like "Rebuild
container" after changes to the devcontainer configuration. The devcontainer-cli
is shipped with the devcontainer image and can be used to rebuild the
devcontainer:

```bash
devcontainer up --workspace-folder /workspaces/visualization-tool --remove-existing-container --build-no-cache
```

⚠️ Supporting/ Using dev-containers is currently under development and not fully
tested, please report if you run into issues.
Loading