Skip to content
Snippets Groups Projects
Commit df40b7be authored by julian.baeume's avatar julian.baeume :pick:
Browse files

Revert "Fix: OXUIB-2698: SSRF/XSS: Prevent to cache resources from external origins"

This reverts commit d3bc298f.
parent ea62b6a9
No related branches found
No related tags found
No related merge requests found
......@@ -30,7 +30,7 @@ describe('File caching service', function () {
let app, pubClient
beforeEach(async function () {
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
......@@ -62,7 +62,7 @@ describe('File caching service', function () {
const version = response.headers.version
const { client } = await import('../src/redis.js')
expect(await client.get(getRedisKey({ version, name: 'viteManifests' }))).to.equal('{"index.html":{"file":"index.html","meta":{"baseUrl":"http://ui-server"}}}')
expect(await client.get(getRedisKey({ version, name: 'viteManifests' }))).to.equal('{"index.html":{"file":"index.html","meta":{"baseUrl":"http://ui-server/"}}}')
const redisData = await client.getBuffer(getRedisKey({ version, name: 'oxManifests:body' }))
expect(zlib.brotliDecompressSync(redisData || '').toString()).to.equal('[]')
})
......@@ -95,9 +95,4 @@ describe('File caching service', function () {
const response2 = await app.inject({ url: '/demo.js', headers: { version } })
expect(response2.statusCode).to.equal(200)
})
it('does not fetch from origins not defined in baseUrls', async function () {
const response = await app.inject({ url: '//t989be0.netlify.app/xss.html' })
expect(response.statusCode).to.equal(400)
})
})
......@@ -32,7 +32,7 @@ describe('Configuration', function () {
beforeEach(async function () {
// need to set the redis-prefix. Otherwise, the bull workers will interfere
process.env.REDIS_PREFIX = Math.random().toString()
await mockConfig(config = { baseUrls: ['http://ui-server'] })
await mockConfig(config = { baseUrls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
......
......@@ -33,7 +33,7 @@ describe('Updates the version', function () {
beforeEach(async function () {
// need to set the redis-prefix. Otherwise, the bull workers will interfere
process.env.REDIS_PREFIX = Math.random().toString()
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
......@@ -109,7 +109,7 @@ describe('Updates the version', function () {
td.reset()
// need to set the redis-prefix. Otherwise, the bull workers will interfere
process.env.REDIS_PREFIX = Math.random().toString()
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
mockFetch({
'http://ui-server': {
'/manifest.json': generateSimpleViteManifest({
......
......@@ -29,7 +29,7 @@ describe('With different app root', function () {
let app
beforeEach(async function () {
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
const { client } = await mockRedis()
mockFetch({
'http://ui-server': {
......
......@@ -37,7 +37,7 @@ describe('File caching service', function () {
let redis
beforeEach(async function () {
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
redis = await mockRedis()
mockFetch({
'http://ui-server': {}
......@@ -273,9 +273,4 @@ describe('File caching service', function () {
// check for files in redis
expect(await redis.client.getBuffer('ui-middleware:554855300:/file.svg:body')).to.deep.equal(response.rawPayload)
})
it('does not fetch from origins not defined in baseUrls', async function () {
const response = await app.inject({ url: '//t989be0.netlify.app/xss.html' })
expect(response.statusCode).to.equal(400)
})
})
......@@ -29,7 +29,7 @@ describe('Redirects', function () {
let app
before(async function () {
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
await mockRedis()
mockFetch({
'http://ui-server': {
......
......@@ -30,7 +30,7 @@ describe('Salt', function () {
let config
beforeEach(async function () {
await mockConfig(config = { baseUrls: ['http://ui-server'] })
await mockConfig(config = { baseUrls: ['http://ui-server/'] })
await mockRedis()
mockFetch({
'http://ui-server': {
......
......@@ -30,7 +30,7 @@ describe('UI Middleware', function () {
let fetchConfig
beforeEach(async function () {
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
await mockRedis()
mockFetch(fetchConfig = {
'http://ui-server': {
......@@ -77,7 +77,7 @@ describe('UI Middleware', function () {
describe('multiple configurations', function () {
beforeEach(async function () {
await mockConfig({ baseUrls: ['http://ui-server', 'http://ui-server2'] })
await mockConfig({ baseUrls: ['http://ui-server/', 'http://ui-server2/'] })
fetchConfig['http://ui-server2'] = {
'/manifest.json': generateSimpleViteManifest({ 'example2.js': 'thing' }),
'/example2.js': ''
......
......@@ -60,12 +60,7 @@ export function mockConfig (obj = {}) {
export function mockFetch (servers = {}) {
td.replace(global, 'fetch', async function ({ origin, pathname }, ...args) {
const response = servers[origin]?.[pathname]
if (response === undefined) {
if (origin === 'http://t989be0.netlify.app') {
return new Response('<html><code>Proof of Concept</code><script>alert(document.domain)</script></html>', { status: 200, headers: { 'Content-Type': 'text/html' } })
}
return new Response('', { status: 404 })
}
if (response === undefined) return new Response('', { status: 404 })
if (response instanceof Function) return response.apply(this, arguments)
if (typeof response === 'object') {
......
......@@ -32,7 +32,7 @@ describe('version mismatches', function () {
let runUpdate
beforeEach(async function () {
await mockConfig({ baseUrls: ['http://ui-server'] })
await mockConfig({ baseUrls: ['http://ui-server/'] })
const { createClient } = await mockRedis()
mockFetch({
'http://ui-server': {
......
......@@ -27,13 +27,6 @@ export class NotFoundError extends Error {
}
}
export class NotAllowedOriginError extends Error {
constructor (message, options = {}) {
super(message, options)
this.status = options.status
}
}
export class VersionMismatchError extends Error {}
/**
......@@ -55,8 +48,3 @@ export function isNotFoundError (err) {
const errors = err instanceof AggregateError ? err.errors : [err]
return errors.some(error => error instanceof NotFoundError)
}
export function isNotAllowedOriginError (err) {
const errors = err instanceof AggregateError ? err.errors : [err]
return errors.some(error => error instanceof NotAllowedOriginError)
}
......@@ -25,7 +25,7 @@ import { promisify } from 'node:util'
import zlib from 'node:zlib'
import * as cache from './cache.js'
import { configMap } from './config_map.js'
import { NotAllowedOriginError, NotFoundError, VersionMismatchError, isVersionMismatchError } from './errors.js'
import { NotFoundError, VersionMismatchError, isVersionMismatchError } from './errors.js'
import logger from './logger.js'
import { getCSSDependenciesFor, getViteManifests } from './manifests.js'
import { getVersionInfo } from './version.js'
......@@ -38,12 +38,8 @@ const compressionMimeTypes = (process.env.COMPRESS_FILE_TYPES || '').replace(/([
const compressionWhitelistRegex = new RegExp(`^(${compressionMimeTypes.join('|')})($|;)`, 'i')
export async function fetchFileWithHeadersFromBaseUrl ({ path, baseUrl, version }) {
const upstreamUrl = new URL(path, baseUrl)
if (upstreamUrl.origin !== baseUrl) {
throw new NotAllowedOriginError('This origin is not allowed', { status: 400 })
}
const [response, dependencies] = await Promise.all([
fetch(upstreamUrl, { cache: 'no-store' }),
fetch(new URL(path, baseUrl), { cache: 'no-store' }),
nodePath.extname(path) === '.js' && getCSSDependenciesFor({ file: path.substr(1), version })
])
......
......@@ -21,7 +21,7 @@
*/
import { getFile } from '../files.js'
import { isNotAllowedOriginError, isNotFoundError, isVersionMismatchError } from '../errors.js'
import { isNotFoundError, isVersionMismatchError } from '../errors.js'
export default async function serveFilePlugin (fastify, options) {
fastify.get('*', async (req, reply) => {
......@@ -35,7 +35,6 @@ export default async function serveFilePlugin (fastify, options) {
reply.send(body)
} catch (err) {
if (isNotFoundError(err) || isVersionMismatchError(err)) throw fastify.httpErrors.createError(404, `File "${req.urlData('path')}" does not exist.`, err)
if (isNotAllowedOriginError(err)) throw fastify.httpErrors.createError(400, err)
throw fastify.httpErrors.createError(err.statusCode || 500, err)
}
})
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment