Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b1ca90f
chore: remove express server
jmfrancois Jan 26, 2026
b90c761
chore: rewrite and fix the build and serve
jmfrancois Jan 26, 2026
094c827
Merge b90c7619bf89fa620c52849ccdf7612e02cc59ad into 45c63d0274629eb84…
jmfrancois Jan 26, 2026
ecbbe14
chore: yarn-deduplicate
github-actions[bot] Jan 26, 2026
f8a3a2d
Potential fix for code scanning alert no. 2706: Uncontrolled data use…
jmfrancois Jan 27, 2026
1df7d3c
Potential fix for code scanning alert no. 2708: Use of externally-con…
jmfrancois Jan 27, 2026
abeb80d
Potential fix for code scanning alert no. 2705: Uncontrolled data use…
jmfrancois Jan 27, 2026
2387952
Merge branch 'master' into jmfrancois/chore/remove-express-server
jmfrancois Jan 28, 2026
ccd6e41
Fix path traversal vulnerability in serveStatic function (#5696)
Copilot Feb 3, 2026
33aa623
chore: prepare release (#5666)
build-travis-ci Jan 28, 2026
452befb
chore: remove prepublishOnly sript
jmfrancois Jan 28, 2026
c828b9f
fix(http): add missing lib-esm folder (#5685)
jmfrancois Jan 28, 2026
899bcb6
fix: msw usage (#5687)
jmfrancois Jan 30, 2026
6ccf883
chore(CI/changeset): do not build if there are changeset files
jmfrancois Jan 30, 2026
b3d49ad
chore(deps): bump changesets/action from 1.5.3 to 1.6.0 (#5681)
dependabot[bot] Jan 30, 2026
1c2d126
chore(deps): bump svg64 from 1.2.0 to 2.0.0 (#5147)
dependabot[bot] Jan 30, 2026
7ee15b4
chore: prepare release (#5688)
build-travis-ci Jan 30, 2026
d081f05
fix(jest): add WritableStream support (#5691)
jmfrancois Jan 30, 2026
1817e9f
chore: prepare release (#5692)
build-travis-ci Jan 30, 2026
cf06a79
chore(CI): count the changeset Readme.md
jmfrancois Jan 30, 2026
771ae20
fix(babel): add plugin-transform-private-methods
jmfrancois Jan 30, 2026
7654edd
chore: prepare release (#5693)
build-travis-ci Jan 30, 2026
66f719c
test: disable Chromatic snapshots for redundant and non-visual storie…
Copilot Jan 30, 2026
963ffa5
fix: re-release package not built due to CI issue
jmfrancois Jan 31, 2026
ad71883
chore(deps): bump chromaui/action from 13 to 14 (#5697)
dependabot[bot] Feb 3, 2026
2535a45
Merge branch 'master' into jmfrancois/chore/remove-express-server
jmfrancois Feb 3, 2026
2a024dc
fix: security server path
jmfrancois Feb 3, 2026
36ffbac
fix: security server path
jmfrancois Feb 3, 2026
b88c5b8
fix: make it works
jmfrancois Feb 3, 2026
ffd7c0f
Merge branch 'master' into jmfrancois/chore/remove-express-server
jmfrancois Feb 3, 2026
96beda4
fix: try again
jmfrancois Feb 3, 2026
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
2 changes: 0 additions & 2 deletions packages/playground-vite/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
var process = { browser: true, env: { NODE_ENV: 'development' } };
</script>
<title>Vite + React</title>
'', '@talend/locales-tui-components', '@talend/locales-tui-containers',
'@talend/locales-tui-faceted-search', '@talend/locales-tui-forms',

<meta name="@talend/locales-design-system" content="7.15.1" />
<meta name="@talend/locales-tui-components" content="16.0.1" />
Expand Down
7 changes: 2 additions & 5 deletions packages/playground-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"type": "module",
"main": "app/index.js",
"scripts": {
"build": "cross-env BASENAME='/playground' talend-scripts build",
"build": "vite build && cmf-settings",
"test": "echo nothing to test in playground",
"test:demo:umd": "cross-env BASENAME='/playground/' INITIATOR_URL='/playground/inject.js' talend-scripts build --prod",
"start": "vite dev",
"start-dist": "talend-scripts build && node serve-dist",
"start-dist": "node serve-dist.mjs",
"lint": "talend-scripts lint"
},
"repository": {
Expand All @@ -31,12 +31,9 @@
"@talend/scripts-config-stylelint": "^4.4.0",
"@talend/scripts-core": "^17.0.0",
"@vitejs/plugin-react": "^4.7.0",
"body-parser": "1.20.3",
"compression": "^1.8.1",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"esbuild-plugin-react-virtualized": "^1.0.5",
"express": "^5.2.1",
"i18next-http-backend": "^1.4.5",
"mockjs": "^1.1.0",
"sass": "^1.97.3",
Expand Down
17 changes: 0 additions & 17 deletions packages/playground-vite/serve-dist.js

This file was deleted.

129 changes: 129 additions & 0 deletions packages/playground-vite/serve-dist.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/* eslint-disable no-console */
/* eslint-disable no-underscore-dangle */
import http from 'http';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const distRoot = path.join(__dirname, 'dist');

const options = process.argv.slice(2);
const useGzip = options.includes('--gzip');

// Simple static file server
function serveStatic(req, res, filePath) {
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
return;
}

const ext = path.extname(filePath);
const contentTypes = {
'.html': 'text/html',
'.js': 'application/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
};

const contentType = contentTypes[ext] || 'application/octet-stream';
const headers = {
'Content-Type': contentType,
'Content-Length': data.length,
};

if (useGzip) {
headers['Content-Encoding'] = 'gzip';
}

res.writeHead(200, headers);
res.end(data);
});
}

function resolveSafeFilePath(requestUrl) {
let pathname;
try {
const urlObj = new URL(requestUrl, 'http://localhost');
pathname = urlObj.pathname || '/';
} catch {
return { statusCode: 400, message: 'Bad Request' };
}

let decodedPathname;
try {
decodedPathname = decodeURIComponent(pathname);
} catch {
return { statusCode: 400, message: 'Bad Request' };
}

if (decodedPathname.includes('\0')) {
return { statusCode: 400, message: 'Bad Request' };
}

const normalizedPathname = path.posix.normalize(decodedPathname.replace(/\\/g, '/'));
if (!normalizedPathname.startsWith('/')) {
return { statusCode: 400, message: 'Bad Request' };
}

// Resolve and normalize the path, then check if file exists and resolve symlinks
let filePath = path.resolve(distRoot, '.' + normalizedPathname);

try {
// Use realpathSync to resolve any symbolic links and get the canonical path
filePath = fs.realpathSync(filePath);
} catch (err) {
// File doesn't exist or can't be accessed, but we'll handle this later with fs.stat
// For now, just ensure the non-canonical path is still within bounds
}

if (!filePath.startsWith(distRoot + path.sep) && filePath !== distRoot) {
return { statusCode: 403, message: 'Forbidden' };
}

return { filePath };
}

const server = http.createServer((req, res) => {
// Serve static files from dist
const { statusCode, message, filePath: resolvedFilePath } = resolveSafeFilePath(req.url);
let filePath = resolvedFilePath;
if (!filePath) {
res.writeHead(statusCode, { 'Content-Type': 'text/plain' });
res.end(message);
return;
}

// Handle directory requests (serve index.html)
fs.stat(filePath, (err, stats) => {
if (!err && stats.isDirectory()) {
filePath = path.join(filePath, 'index.html');

// Re-validate the path after appending index.html
try {
const realPath = fs.realpathSync(filePath);
if (!realPath.startsWith(distRoot + path.sep) && realPath !== distRoot) {
res.writeHead(403, { 'Content-Type': 'text/plain' });
res.end('Forbidden');
return;
}
filePath = realPath;
} catch {
// File doesn't exist, will be handled by serveStatic with 404
}
}

serveStatic(req, res, filePath);
});
});

server.listen(3000, () => {
console.log('ready http://localhost:3000');
});
34 changes: 21 additions & 13 deletions packages/playground/mockBackend/jsonForward.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,28 @@ const wait = (delay = 1000) => new Promise(resolve => setTimeout(resolve, delay)
/**
* Directly bind /api/mock/* HTTP queries to local mockBackend/mock/* contents
*/
module.exports = function addRoutes(app) {
module.exports = function addRoutes(req, res) {
const API_MOCK_ENDPOINT = '/api/mock';

app.get(`${API_MOCK_ENDPOINT}/*`, (req, res) => {
const urlPath = req.url.split('?')[0];
const mockFilePath = `${__dirname}/mock/${urlPath.substr(API_MOCK_ENDPOINT.length)}.json`;
if (!req.url.startsWith(API_MOCK_ENDPOINT)) {
res.writeHead(404);
res.end('Not Found');
return;
}

wait()
.then(() => readFile(mockFilePath))
.then(content => res.json(JSON.parse(content)))
.catch(error => {
// eslint-disable-next-line no-console
console.error(`Unable to load mock file "${mockFilePath}" due to :`, error);
res.status(400).send('Bad Request');
});
});
const urlPath = req.url.split('?')[0];
const mockFilePath = `${__dirname}/mock/${urlPath.substr(API_MOCK_ENDPOINT.length)}.json`;

wait()
.then(() => readFile(mockFilePath))
.then(content => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(JSON.parse(content)));
})
.catch(error => {
// eslint-disable-next-line no-console
console.error('Unable to load mock file "%s" due to :', mockFilePath, error);
res.writeHead(400, { 'Content-Type': 'text/plain' });
res.end('Bad Request');
});
};
48 changes: 29 additions & 19 deletions packages/playground/mockBackend/kit.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
const url = require('url');
const http = require('https');
const forms = require('./mock/kit');
Expand Down Expand Up @@ -91,7 +92,8 @@ function suggestionBig() {
cacheable: true,
items: JSON.parse(body).map(item => ({ id: item.id.toString(), label: item.title })),
};
res.json(cache.photos);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(cache.photos));
}
function onResponse(resp) {
console.log(`Got response: ${resp.statusCode}`);
Expand Down Expand Up @@ -121,14 +123,17 @@ function updateProperties({ type }) {

function giveMeFive() {
return res => {
res.status(500).json({
timestamp: 1548781374412,
status: 500,
error: 'Internal Server Error',
exception: 'javax.ws.rs.ClientErrorException',
message: 'An internal server error occurs',
path: '/proxy/v1/action/execute/dataset',
});
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
timestamp: 1548781374412,
status: 500,
error: 'Internal Server Error',
exception: 'javax.ws.rs.ClientErrorException',
message: 'An internal server error occurs',
path: '/proxy/v1/action/execute/dataset',
}),
);
};
}

Expand Down Expand Up @@ -162,19 +167,24 @@ function trigger(req) {
return TRIGGERS[info.type][info.action](info.args);
}

module.exports = function addRoutes(app) {
app.get('/api/v1/forms/:formId', (req, res) => {
res.json(forms[req.params.formId]);
});
app.post('/api/v1/forms', (req, res) => {
res.json({ body: req.body });
});
app.post('/api/v1/application/action', (req, res) => {
module.exports = function addRoutes(req, res) {
if (req.url.startsWith('/api/v1/forms/')) {
const formId = req.url.split('/')[4];
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(forms[formId]));
} else if (req.url === '/api/v1/forms' && req.method === 'POST') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ body: req.body }));
} else if (req.url === '/api/v1/application/action' && req.method === 'POST') {
const result = trigger(req);
if (typeof result === 'function') {
result(res);
} else {
res.json(result);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
}
});
} else {
res.writeHead(404);
res.end('Not Found');
}
};
32 changes: 27 additions & 5 deletions packages/playground/mockBackend/server.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
const bodyParser = require('body-parser');
const kit = require('./kit');
const jsonForward = require('./jsonForward');

const server = devServer => {
devServer.app.use(bodyParser.json()); // for parsing application/json
jsonForward(devServer.app);
kit(devServer.app);
const server = (req, res) => {
// Parse JSON body for POST/PUT requests
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
req.body = body ? JSON.parse(body) : {};
} catch (e) {
req.body = {};
}
// Parse query string
const url = new URL(req.url, `http://${req.headers.host}`);
req.query = Object.fromEntries(url.searchParams);
req.url = url.pathname;

// Route to appropriate handler
if (req.url.startsWith('/api/mock/')) {
jsonForward(req, res);
} else if (req.url.startsWith('/api/')) {
kit(req, res);
} else {
res.writeHead(404);
res.end('Not Found');
}
});
};

module.exports = server;
4 changes: 0 additions & 4 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,8 @@
"@talend/scripts-core": "^17.0.0",
"@talend/scripts-config-babel": "^13.9.0",
"@talend/scripts-config-stylelint": "^4.4.0",
"body-parser": "1.20.3",
"compression": "^1.8.1",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"express": "^5.2.1",
"i18next-http-backend": "^1.4.5",
"webpack": "^5.104.1"
},
"dependencies": {
Expand Down
Loading
Loading