E12050: service requires 'open' or 'constrain by' security posture declaration

Category: E12xxx Security constraints · Reported in phase: SYMBOL_DEFINITION · Classification: SERVICE_SECURITY_NOT_DECLARED · All error codes

What went wrong

Every service operation must have a security posture, either inherited from its parent service or declared on the operation itself. The compiler found a service operation with no posture. If the service declares 'open' or 'constrain by X', all operations inherit automatically. If the service has no posture, each operation must declare its own 'open' or 'constrain by'.

Worked examples

The [EXAMPLE] section below shows a service with no security posture, then the posture given at the service level - 'open', or 'constrain by' a security gate and/or a CORS policy, which every operation inherits - and at the operation level, when the service declares none (good_operation.ek9: one operation 'open', another 'constrain by ApiGate').

Rationale

Security posture must be a conscious, visible decision. A service that silently defaults to 'open' creates a class of bugs where developers forget to add authentication. Requiring explicit declaration means the security posture is always auditable in the source code.

This follows the EK9 principle: if the compiler cannot verify it, it does not compile. There is no 'default open' escape hatch.

How to fix it

Add a security posture to the service or to each operation. A service with no posture anywhere is this error. At the service level, 'open' or 'constrain by' a gate (and/or a CORS policy) is inherited by every operation; when the service declares none, each operation declares its own ('open', or 'constrain by ApiGate').

See also

  • Constraint type resolution: E12060
  • Constraint type validation: E12062
  • Duplicate constraint role: E12064
  • SecurityGate abstract function: ek9 -h SecurityGate
  • CORSPolicy abstract function: ek9 -h CORSPolicy
  • Compilable examples: ek9 -q 1292 (How does service security posture work with 'open' and ...) ek9 -q 1293 (What happens when a constrain by reference cannot be re...) ek9 -q 1294 (What types are valid in a constrain by clause?) ek9 -q 1295 (Why does constrain by X and Y fail when both extend Sec...)

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E12050: service requires 'open' or 'constrain by' security posture declaration

#!ek9
defines module error.e12050.bad

  defines service

    NoPostureService for :/api
      health() as GET for :/status
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("ok")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

Fixed: Declare the posture: 'open', or 'constrain by' a gate

#!ek9
defines module error.e12050.good

  defines service

    PostureService for :/api open

      health() as GET for :/status
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("ok")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

Fixed: Or declare the posture on each operation

#!ek9
defines module error.e12050.good.operation

  defines function

    ApiGate is SecurityGate
      -> context as HTTPContext
      <- rtn as HTTPContext: context

  defines service

    PostureService for :/api
      health() as GET for :/status open
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("ok")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

      report() as GET for :/report constrain by ApiGate
        <- response as HTTPResponse: () with trait of HTTPResponse
          override status() as pure
            <- rtn as Integer: 200
          override content()
            <- rtn as Bytes: Bytes().withUtf8("report")
          override contentType() as pure
            <- rtn as String: "text/plain"
          default operator ?

Questions and answers

These Q&A entries show E12050 and its fix: