fix: allow explicit HTTP origin in test environments
This commit is contained in:
@@ -10,6 +10,8 @@ REDIS_URL=redis://127.0.0.1:6379
|
|||||||
HTTP_API_MASTER_KEY=replace-with-at-least-32-random-characters
|
HTTP_API_MASTER_KEY=replace-with-at-least-32-random-characters
|
||||||
# Customer-facing HTTP API origin returned by the real backend and shown in copied integration parameters.
|
# Customer-facing HTTP API origin returned by the real backend and shown in copied integration parameters.
|
||||||
HTTP_API_PUBLIC_ORIGIN=https://api.example.com
|
HTTP_API_PUBLIC_ORIGIN=https://api.example.com
|
||||||
|
# Keep false in production. Only isolated test environments without TLS may opt in to HTTP.
|
||||||
|
HTTP_API_ALLOW_INSECURE_ORIGIN=false
|
||||||
API_ENABLE_SEND_WORKER=true
|
API_ENABLE_SEND_WORKER=true
|
||||||
API_SEND_WORKER_CONCURRENCY=50
|
API_SEND_WORKER_CONCURRENCY=50
|
||||||
API_WORKER_DATABASE_URL=
|
API_WORKER_DATABASE_URL=
|
||||||
|
|||||||
@@ -26,6 +26,27 @@ describe('OpenApiService', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('only permits a plain HTTP public origin when an isolated test environment explicitly opts in', async () => {
|
||||||
|
const previousOrigin = process.env.HTTP_API_PUBLIC_ORIGIN;
|
||||||
|
const previousAllowInsecure = process.env.HTTP_API_ALLOW_INSECURE_ORIGIN;
|
||||||
|
process.env.HTTP_API_PUBLIC_ORIGIN = 'http://100.93.204.60:12026/';
|
||||||
|
delete process.env.HTTP_API_ALLOW_INSECURE_ORIGIN;
|
||||||
|
const prisma = {
|
||||||
|
smsApplication: { findFirst: jest.fn().mockResolvedValue({ id: 'app-1', name: '应用A', httpConfig: null, httpIpAllowlist: [] }) },
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const service = new OpenApiService(prisma as never, {} as never);
|
||||||
|
await expect(service.getConfig('app-1')).rejects.toThrow('HTTP_API_ALLOW_INSECURE_ORIGIN');
|
||||||
|
process.env.HTTP_API_ALLOW_INSECURE_ORIGIN = 'true';
|
||||||
|
await expect(service.getConfig('app-1')).resolves.toEqual(expect.objectContaining({ publicOrigin: 'http://100.93.204.60:12026' }));
|
||||||
|
} finally {
|
||||||
|
if (previousOrigin === undefined) delete process.env.HTTP_API_PUBLIC_ORIGIN;
|
||||||
|
else process.env.HTTP_API_PUBLIC_ORIGIN = previousOrigin;
|
||||||
|
if (previousAllowInsecure === undefined) delete process.env.HTTP_API_ALLOW_INSECURE_ORIGIN;
|
||||||
|
else process.env.HTTP_API_ALLOW_INSECURE_ORIGIN = previousAllowInsecure;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('replays a completed request for the same idempotency key and body', async () => {
|
it('replays a completed request for the same idempotency key and body', async () => {
|
||||||
const prisma = {
|
const prisma = {
|
||||||
openApiRequest: { findUnique: jest.fn().mockResolvedValue({ bodyHash: 'same', status: 'completed', responseBody: { code: 'ACCEPTED', messageId: 'MSG-1' } }) },
|
openApiRequest: { findUnique: jest.fn().mockResolvedValue({ bodyHash: 'same', status: 'completed', responseBody: { code: 'ACCEPTED', messageId: 'MSG-1' } }) },
|
||||||
|
|||||||
@@ -428,10 +428,12 @@ function httpApiPublicOrigin() {
|
|||||||
const configured = process.env.HTTP_API_PUBLIC_ORIGIN?.trim().replace(/\/+$/, '');
|
const configured = process.env.HTTP_API_PUBLIC_ORIGIN?.trim().replace(/\/+$/, '');
|
||||||
if (!configured) return undefined;
|
if (!configured) return undefined;
|
||||||
const url = new URL(configured);
|
const url = new URL(configured);
|
||||||
if (url.protocol !== 'https:' || url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
|
const insecureHttpExplicitlyAllowed = process.env.HTTP_API_ALLOW_INSECURE_ORIGIN === 'true' && url.protocol === 'http:';
|
||||||
|
if ((url.protocol !== 'https:' && !insecureHttpExplicitlyAllowed) || url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
|
||||||
// This value is copied into customer integration parameters, so fail closed instead of
|
// This value is copied into customer integration parameters, so fail closed instead of
|
||||||
// publishing an insecure or path-dependent endpoint when deployment config is wrong.
|
// publishing an insecure or path-dependent endpoint unless an isolated test environment
|
||||||
throw new Error('HTTP_API_PUBLIC_ORIGIN必须是无路径、无凭据的HTTPS源地址');
|
// has explicitly opted into plain HTTP.
|
||||||
|
throw new Error('HTTP_API_PUBLIC_ORIGIN必须是无路径、无凭据的HTTPS源地址;隔离测试环境如需HTTP须显式启用HTTP_API_ALLOW_INSECURE_ORIGIN');
|
||||||
}
|
}
|
||||||
return url.origin;
|
return url.origin;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user