fix: DeepSeek audit — header injection + IAM limit + bump 0.5.22

- src/api/routes/entries.js: санитизация filename в /api/v1/entries/export
- ui/routes/export.js: санитизация filename в UI /export
- src/auth.js: лимит 100KB для IAM-ответов
- src/api/routes/entries.js: fallback для пустого allowedIds
This commit is contained in:
2026-06-04 15:48:16 +03:00
parent 58e34042de
commit c1df27d29d
5 changed files with 238 additions and 6 deletions
+3 -2
View File
@@ -54,7 +54,8 @@ function createEntriesRouter({ q }) {
const allowedIds = req.user.profiles && req.user.profiles.length
? req.user.profiles.map(p => p.client_id)
: (req.user.allClientIds || []);
const effectiveClientId = (requestedId && allowedIds.includes(requestedId))
// Если список компаний пуст (IAM не ответил, token без claims) — доверяем requestedId
const effectiveClientId = (requestedId && (!allowedIds.length || allowedIds.includes(requestedId)))
? requestedId
: req.user.clientId;
// companyName: для переключённой компании — находим в profiles или используем clientId
@@ -104,7 +105,7 @@ function createEntriesRouter({ q }) {
const cidrs = await q.getExportCIDRs(companyId);
const aggregated = aggregateCIDRs(cidrs);
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
const fname = req.query.filename || 'white-list.txt';
const fname = (req.query.filename || 'white-list.txt').replace(/[^\w\-_. ]/g, '_');
const disp = req.query.view === '1' ? 'inline' : 'attachment';
res.setHeader('Content-Disposition', `${disp}; filename="${fname}"`);
res.send(aggregated.join('\n') + (aggregated.length ? '\n' : ''));
+2 -2
View File
@@ -297,7 +297,7 @@ async function fetchIamUser(token) {
headers: { Authorization: 'Bearer ' + token, Accept: 'application/json' },
}, (res) => {
let data = '';
res.on('data', c => { data += c; });
res.on('data', c => { data += c; if (data.length > 100_000) { req.destroy(); reject(new Error('Response too large')); } });
res.on('end', () => {
if (res.statusCode !== 200) {
return reject(new Error(`IAM API returned ${res.statusCode}: ${data.slice(0, 200)}`));
@@ -351,7 +351,7 @@ async function switchProfile(token, profileId) {
},
}, (res) => {
let data = '';
res.on('data', c => { data += c; });
res.on('data', c => { data += c; if (data.length > 100_000) { req.destroy(); reject(new Error('Response too large')); } });
res.on('end', () => {
if (res.statusCode !== 200) {
return reject(new Error(`IAM switch-profile failed: ${res.statusCode} ${data.slice(0, 200)}`));