fix: align nginx with production smoke gate

This commit is contained in:
2026-09-02 11:05:34 +03:00
parent 0119e425ec
commit 259f52a02b
3 changed files with 47 additions and 3 deletions

View File

@@ -9,6 +9,7 @@ import {
} from '../../scripts/generate-host-config.mjs';
const jsonLdHash = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
const releaseManifestDigest = 'd'.repeat(64);
const productionSite: SiteConfig = {
environment: 'production',
canonicalUrl: 'https://portfolio.example.test/',
@@ -38,6 +39,7 @@ describe('generated Nginx static-host contract', () => {
alternateHosts: ['www.portfolio.example.test'],
tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem',
tlsKeyPath: '/etc/portfolio/tls/privkey.pem',
manifestDigest: releaseManifestDigest,
});
for (const file of manifest.files) expect(config).toContain(`~^/${file.path.replaceAll('.', '\\.')}$`);
@@ -62,6 +64,7 @@ describe('generated Nginx static-host contract', () => {
alternateHosts: ['www.portfolio.example.test'],
tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem',
tlsKeyPath: '/etc/portfolio/tls/privkey.pem',
manifestDigest: releaseManifestDigest,
});
expect(config).toContain('listen 80;');
@@ -80,12 +83,18 @@ describe('generated Nginx static-host contract', () => {
alternateHosts: [],
tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem',
tlsKeyPath: '/etc/portfolio/tls/privkey.pem',
manifestDigest: releaseManifestDigest,
});
expect(config).toContain("default-src 'self'; script-src 'self' 'sha256-");
expect(config).toContain("img-src 'self' data:");
expect(config).toContain("connect-src 'none'");
expect(config).toContain('add_header Strict-Transport-Security "max-age=31536000" always;');
expect(config).toContain(`add_header X-Portfolio-Manifest-Digest "${releaseManifestDigest}" always;`);
expect(config).toContain('gzip on;');
expect(config).toContain('gzip_min_length 1;');
expect(config).toContain('gzip_vary on;');
expect(config).toContain('gzip_types text/css application/javascript application/json image/svg+xml;');
expect(config).not.toContain('includeSubDomains');
expect(config).not.toContain('X-Robots-Tag');
expect(config).not.toMatch(/unsafe-inline|unsafe-eval|sourceMappingURL|\.map(?:\s|$)/u);
@@ -104,6 +113,19 @@ describe('generated Nginx static-host contract', () => {
expect(config).toContain('private, no-store');
expect(config).not.toContain('Strict-Transport-Security');
expect(config).not.toContain('sitemap.xml');
expect(config).not.toContain('X-Portfolio-Manifest-Digest');
});
it('rejects production configuration without the signed manifest digest', () => {
expect(() => renderNginxConfig({
site: productionSite,
jsonLdCspHash: jsonLdHash,
artifactManifest: manifest,
documentRoot: '/srv/portfolio/current',
alternateHosts: [],
tlsCertificatePath: '/etc/portfolio/tls/fullchain.pem',
tlsKeyPath: '/etc/portfolio/tls/privkey.pem',
})).toThrow(/manifest digest/i);
});
it.each([
@@ -149,6 +171,7 @@ describe('generated Nginx static-host contract', () => {
expect(example).toContain('__CANONICAL_HOST__');
expect(example).toContain('__JSON_LD_CSP_HASH__');
expect(example).toContain('__MANIFEST_DIGEST__');
expect(example).not.toContain('portfolio.example.test');
expect(example).not.toMatch(/server_name\s+[a-z0-9.-]+\.[a-z]{2,}/iu);
});