FQL injection

For serverless clients, it’s a common pattern to allow users to call UDF only and give UDF admin-like permissions. The injection I’m afraid of can come from not validated args. This is the code I am using.

const _TrimmedString = (value: ExprVal) =>
  Let(
    { trimmed: Trim(value) },
    If(
      Equals(Length(value), Length(Var('trimmed'))),
      value,
      Abort('TrimmedString'),
    ),
  );

const _NonEmptyString = (value: ExprVal) =>
  If(GT(Length(value), 0), value, Abort('NonEmptyString'));

const _Max64String = (value: ExprVal) =>
  If(LTE(Length(value), 64), value, Abort('Max64String'));

// const _Max1024String = (value: ExprVal) =>
//   If(LTE(Length(value), 1024), value, Abort('Max1024String'));

const _Min6String = (value: ExprVal) =>
  If(GTE(Length(value), 6), value, Abort('Min6String'));

const String64 = flow(_TrimmedString, _NonEmptyString, _Max64String);

// All args must be validated, because functions have admin role!
const Arg = (prop: ExprArg, Type: (value: ExprVal) => Expr): Expr =>
  pipe(Select(prop, Var('args')), Type);
const ArgRef = (prop: ExprArg): Expr =>
  pipe(Select(prop, Var('args')), (value) =>
    If(IsRef(value), value, Abort('argRef')),
  );

updateViewerName: EitherRight(
  Update(ViewerRef(), {
    data: {
      name: Arg('name', String64),
    },
  }),
),

But FQL can be tricky I guess. What if someone replaces the string with an FQL expression which does some side effects which evaluate to string? How can we detect that?

In the other words, is my approach correct?

1 Like

I mean such code:

IsString(Do(1, ''))

It makes IsString effectively pointless to validate inputs. Such expressions evaluation makes FQL injection super easy. Am I correct?

Hi @Daniel_Steigerwald! There are no FQL injection issues that allow a user to run any FQL with permissions that they don’t already have.

Yes! And it’s totally protected from injection.

For example with some function like this:

CreateFunction({
  name: "fn_does_admin_things",
  role: "admin",
  body: Query(Lambda(
    ["input"],
    /* does something with the provided input */
  ))
})

if a user tries to call it with some attempt at malicious code

Call(
  Function("fn_does_admin_things"),
  Do(
    // destroy all the things muahahahahaha!
    Map(
      Paginate(Collections()),
      ref => Delete(ref)
    ),
    "totally benign string"
  )
)

all of what you see in the lower code block is run with the user/caller’s permissions only, NOT the functions permissions. FQL is not “lazy”. The argument will be run before the call. In the case above, the user doesn’t have permission to access Collections() so it will cause an error.

1 Like

OK, it makes sense. Thank you for the explanation that FQL is not lazily evaluated.

1 Like

Sure! I get the concern – getting the the perfect Roles put in place can be enough of a challenge, you don’t want to worry about user abusing the query language, too :slightly_smiling_face:

I hope that it does clear things up. If you have any more concerns/reservations about something please feel free to speak up.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.