fix: enhance security with strict file permissions, pin isolation, and robust protocol validations

This commit is contained in:
Claudecio Martins
2026-10-11 21:47:35 -03:00
parent f4adc99a01
commit 64ca13b3a1
11 changed files with 336 additions and 93 deletions
+27 -7
View File
@@ -4,6 +4,25 @@
* de temas de selos de assinatura digital PAdES (ICP-Brasil).
*/
function escapeSvgText(str) {
if (str === null || str === undefined) return '';
return String(str)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
}
function sanitizeLogoUrl(url) {
if (!url || typeof url !== 'string') return '';
const trimmed = url.trim();
if (/^data:image\/(png|jpeg|jpg|svg\+xml);base64,[A-Za-z0-9+/=]+$/i.test(trimmed)) {
return trimmed;
}
return '';
}
export class PdfSealDesigner {
/**
* @param {Object} options Configurações iniciais do designer.
@@ -592,8 +611,9 @@ export class PdfSealDesigner {
}
let iconGraphic = '';
if (t.customLogoBase64) {
iconGraphic = `<image xlink:href="${t.customLogoBase64}" href="${t.customLogoBase64}" x="12" y="14" width="34" height="54" preserveAspectRatio="xMidYMid meet" />`;
const safeLogo = sanitizeLogoUrl(t.customLogoBase64);
if (safeLogo) {
iconGraphic = `<image xlink:href="${safeLogo}" href="${safeLogo}" x="12" y="14" width="34" height="54" preserveAspectRatio="xMidYMid meet" />`;
} else {
switch (t.iconStyle) {
case 'star':
@@ -640,11 +660,11 @@ export class PdfSealDesigner {
</g>
<!-- Tipografia do Selo -->
<text x="54" y="19" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8.5" font-weight="bold" fill="${t.primaryColor}" letter-spacing="0.3">${t.customHeader || 'DOCUMENTO ASSINADO DIGITALMENTE'}</text>
<text x="54" y="34" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="bold" fill="${t.textColor}">MARIA DA SILVA</text>
<text x="54" y="47" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">${[docText, extraText].filter(Boolean).join(' • ')}</text>
<text x="54" y="58" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">${dateText ? `Data: ${dateText}` : ''}</text>
<text x="54" y="70" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="7.5" fill="#64748b">${t.customFooter || 'ICP-Brasil • MP nº 2.200-2/2001'}</text>
<text x="54" y="19" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8.5" font-weight="bold" fill="${escapeSvgText(t.primaryColor)}" letter-spacing="0.3">${escapeSvgText(t.customHeader || 'DOCUMENTO ASSINADO DIGITALMENTE')}</text>
<text x="54" y="34" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="12" font-weight="bold" fill="${escapeSvgText(t.textColor)}">MARIA DA SILVA</text>
<text x="54" y="47" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">${escapeSvgText([docText, extraText].filter(Boolean).join(' • '))}</text>
<text x="54" y="58" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">${escapeSvgText(dateText ? `Data: ${dateText}` : '')}</text>
<text x="54" y="70" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="7.5" fill="#64748b">${escapeSvgText(t.customFooter || 'ICP-Brasil • MP nº 2.200-2/2001')}</text>
</svg>
`;
+27 -7
View File
@@ -4,6 +4,25 @@
* e geração do payload de assinatura em tempo real.
*/
function escapeSvgText(str) {
if (str === null || str === undefined) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
}
function sanitizeLogoUrl(url) {
if (!url || typeof url !== 'string') return '';
const trimmed = url.trim();
if (/^data:image\/(png|jpeg|jpg|svg\+xml);base64,[A-Za-z0-9+/=]+$/i.test(trimmed)) {
return trimmed;
}
return '';
}
export class PdfStampPicker {
/**
* @param {Object} options Configurações do componente.
@@ -949,8 +968,9 @@ export class PdfStampPicker {
else if (name.length > 26) nameFontSize = 10.0;
let iconGraphic = '';
if (theme.customLogoBase64) {
iconGraphic = `<image xlink:href="${theme.customLogoBase64}" href="${theme.customLogoBase64}" x="12" y="14" width="34" height="54" preserveAspectRatio="xMidYMid meet" />`;
const safeLogo = sanitizeLogoUrl(theme.customLogoBase64);
if (safeLogo) {
iconGraphic = `<image xlink:href="${safeLogo}" href="${safeLogo}" x="12" y="14" width="34" height="54" preserveAspectRatio="xMidYMid meet" />`;
} else {
switch (theme.iconStyle || theme.id) {
case 'star':
@@ -1004,11 +1024,11 @@ export class PdfStampPicker {
</g>
<!-- Tipografia do Selo -->
<text x="54" y="19" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8.5" font-weight="bold" fill="${theme.primaryColor || '#0d47a1'}" letter-spacing="0.3">${header}</text>
<text x="54" y="34" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="${nameFontSize}" font-weight="bold" fill="${theme.textColor || '#0f172a'}">${name}</text>
<text x="54" y="47" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">${doc}</text>
<text x="54" y="58" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">Data: ${dateText}</text>
<text x="54" y="70" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="7.5" fill="#64748b">${footer}</text>
<text x="54" y="19" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8.5" font-weight="bold" fill="${escapeSvgText(theme.primaryColor || '#0d47a1')}" letter-spacing="0.3">${escapeSvgText(header)}</text>
<text x="54" y="34" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="${nameFontSize}" font-weight="bold" fill="${escapeSvgText(theme.textColor || '#0f172a')}">${escapeSvgText(name)}</text>
<text x="54" y="47" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">${escapeSvgText(doc)}</text>
<text x="54" y="58" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="8" fill="#475569">Data: ${escapeSvgText(dateText)}</text>
<text x="54" y="70" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-size="7.5" fill="#64748b">${escapeSvgText(footer)}</text>
</svg>
`;
+2 -1
View File
@@ -136,7 +136,8 @@ export class CyberCoreSigner {
* @returns {Promise<Array<Object>>} Lista de certificados com metadados públicos.
*/
async getCertificates() {
const resp = await this._fetch('/ccsp/v1/certificates', {
const url = `/ccsp/v1/certificates?application_id=${encodeURIComponent(this.applicationId)}`;
const resp = await this._fetch(url, {
method: 'GET'
});