Merge pull request #15556 from Budibase/app-export-vuln
ensure app ownership middleware
This commit is contained in:
commit
630ed8c1ea
|
@ -2,12 +2,14 @@ import Router from "@koa/router"
|
|||
import * as controller from "../controllers/backup"
|
||||
import authorized from "../../middleware/authorized"
|
||||
import { permissions } from "@budibase/backend-core"
|
||||
import ensureTenantAppOwnership from "../../middleware/ensureTenantAppOwnership"
|
||||
|
||||
const router: Router = new Router()
|
||||
|
||||
router.post(
|
||||
"/api/backups/export",
|
||||
authorized(permissions.BUILDER),
|
||||
ensureTenantAppOwnership,
|
||||
controller.exportAppDump
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { tenancy, utils } from "@budibase/backend-core"
|
||||
import { UserCtx } from "@budibase/types"
|
||||
|
||||
async function ensureTenantAppOwnership(ctx: UserCtx, next: any) {
|
||||
const appId = await utils.getAppIdFromCtx(ctx)
|
||||
if (!appId) {
|
||||
ctx.throw(400, "appId must be provided")
|
||||
}
|
||||
const tenantId = tenancy.getTenantId()
|
||||
if (appId !== tenantId) {
|
||||
ctx.throw(403, `App does not belong to tenant`)
|
||||
}
|
||||
await next()
|
||||
}
|
||||
|
||||
export default ensureTenantAppOwnership
|
|
@ -0,0 +1,75 @@
|
|||
import ensureTenantAppOwnership from "../ensureTenantAppOwnership"
|
||||
import { tenancy, utils } from "@budibase/backend-core"
|
||||
|
||||
jest.mock("@budibase/backend-core", () => ({
|
||||
tenancy: {
|
||||
getTenantId: jest.fn(),
|
||||
},
|
||||
utils: {
|
||||
getAppIdFromCtx: jest.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
class TestConfiguration {
|
||||
constructor(appId = "tenant_1") {
|
||||
this.next = jest.fn()
|
||||
this.throw = jest.fn()
|
||||
this.middleware = ensureTenantAppOwnership
|
||||
|
||||
this.ctx = {
|
||||
next: this.next,
|
||||
throw: this.throw,
|
||||
}
|
||||
|
||||
utils.getAppIdFromCtx.mockResolvedValue(appId)
|
||||
}
|
||||
|
||||
async executeMiddleware() {
|
||||
return this.middleware(this.ctx, this.next)
|
||||
}
|
||||
|
||||
afterEach() {
|
||||
jest.clearAllMocks()
|
||||
}
|
||||
}
|
||||
|
||||
describe("Ensure Tenant Ownership Middleware", () => {
|
||||
let config
|
||||
|
||||
beforeEach(() => {
|
||||
config = new TestConfiguration()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
config.afterEach()
|
||||
})
|
||||
|
||||
it("calls next() when appId matches tenant ID", async () => {
|
||||
tenancy.getTenantId.mockReturnValue("tenant_1")
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(utils.getAppIdFromCtx).toHaveBeenCalledWith(config.ctx)
|
||||
expect(config.next).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("throws 403 when appId does not match tenant ID", async () => {
|
||||
tenancy.getTenantId.mockReturnValue("tenant_2")
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(utils.getAppIdFromCtx).toHaveBeenCalledWith(config.ctx)
|
||||
expect(config.throw).toHaveBeenCalledWith(
|
||||
403,
|
||||
"App does not belong to tenant"
|
||||
)
|
||||
})
|
||||
|
||||
it("throws 400 when appId is missing", async () => {
|
||||
utils.getAppIdFromCtx.mockResolvedValue(null)
|
||||
|
||||
await config.executeMiddleware()
|
||||
|
||||
expect(config.throw).toHaveBeenCalledWith(400, "appId must be provided")
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue