Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FR, Bug Report - Guard chaining #1045

Open
Heniker opened this issue Feb 2, 2025 · 3 comments
Open

FR, Bug Report - Guard chaining #1045

Heniker opened this issue Feb 2, 2025 · 3 comments
Labels
enhancement New feature or request

Comments

@Heniker
Copy link

Heniker commented Feb 2, 2025

What is the problem this feature would solve?

Elysia handles multiple .guard calls very strangely.

See the following example:

import { Elysia, t } from 'elysia'

const OnlyStrongPassword = new Elysia()
  .guard({
    body: t.Object({ password: t.Literal('strong-password') }, { additionalProperties: true }),
    beforeHandle() {
      console.log('Checking password...')
    },
  })
  .as('plugin')

const app = new Elysia()
  .use(OnlyStrongPassword)
  .guard({
    body: t.Object({ username: t.Literal('Jhon') }, { additionalProperties: true }),
    beforeHandle() {
      console.log('Checking name...')
    },
  })
  .post('/test', ({ body }) => {
    return 'Hello ' + body.username
  })
  .listen(3000)

const response = await app
  .handle(
    new Request('http://localhost/test', {
      // notice how password is wrong
      body: JSON.stringify({ password: 'weak-password', username: 'Jhon' }),
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
    })
  )
  .then((res) => res.text())

console.log(response)

This logs:

Checking password...
Checking name...
Hello Jhon

Despite provided password being wrong.

What is the feature you are proposing to solve the problem?

Elysia already partially handles combining multiple guards - e.g. it's possible to use query in one guard and body in the other correctly.

The proposed solution is to allow .guard chaining by turning resulting value into intersection.

Using t.Intersect instead of replacing value in mergeHook function worked fine for my use-case.

export const mergeHook = (

The types in MergeSchema must also be updated.

export interface MergeSchema<

What alternatives have you considered?

Throw Error or warn about chaining multiple .guard calls.

@Heniker Heniker added the enhancement New feature or request label Feb 2, 2025
@Heniker Heniker changed the title Guard chaining. FR, Bug Report. FR, Bug Report - Guard chaining Feb 2, 2025
@hisamafahri
Copy link
Contributor

I think it's expected as far as my knowledge goes.
You need to move the username guard to be the same level as the password guards, a plugin, to merge it together (instead of replacing it).

You can try this:

import { Elysia, t } from "elysia";

const passwordGuard = new Elysia().guard({
  body: t.Object({
    password: t.Literal("secure-password"),
  }, { additionalProperties: true }),
}).as('plugin')

const bodyGuard = new Elysia()
  .guard({
    body: t.Object({
      name: t.Literal("John"),
    }, { additionalProperties: true }),
  }).as('plugin')

const app = new Elysia()
  .use(bodyGuard)
  .use(passwordGuard)
  .post("/hello", ({ body }) => {
    return body;
  });

app.listen(9000);

const response = await app
  .handle(
    new Request("http://localhost:9000/hello", {
      body: JSON.stringify({ password: "weak-password", username: "Jhon" }),
      method: "POST",
      headers: { "Content-Type": "application/json" },
    }),
  )
  .then((res) => res.text());

console.log(response);

It should return something like this:


{
  "type": "validation",
  "on": "body",
  "summary": "Expected 'secure-password'",
  "property": "/password",
  "message": "Expected 'secure-password'",
...

@Heniker
Copy link
Author

Heniker commented Feb 8, 2025

No, that doesn't really work.

const response = await app
  .handle(
    new Request("http://localhost:9000/hello", {
      // now username is wrong, but request still passes
      body: JSON.stringify({ password: "secure-password", username: "Bad Username" }),
      method: "POST",
      headers: { "Content-Type": "application/json" },
    }),
  )
  .then((res) => res.text());

console.log(response);

@SaltyAom
Copy link
Member

Hi, this happens because Elysia overrides the properties of a hook when it is a type (in this case "body").

This means it only checks if the username is valid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants