feat(core): add asyncapi security-defined lint rule#2759
Conversation
🦋 Changeset detectedLatest commit: ffc7ef8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
vadyvas
left a comment
There was a problem hiding this comment.
I would suggest a slightly different approach:
- keep the AsyncAPI logic separate and do not reuse shared logic from the OAS rule
- use the same rule name,
security-defined, for AsyncAPI as well, and register the AsyncAPI implementation in the AsyncAPI ruleset - do not update the v1 docs in this PR
I think this would make the change smaller, clearer, and safer.
Thank you for the contribution, overall the PR looks good
vadyvas
left a comment
There was a problem hiding this comment.
left a few comments, could you take a look?
| 'info-contact': InfoContact as Async2Rule, | ||
| 'info-license-strict': InfoLicenseStrict as Async2Rule, | ||
| 'operation-operationId': OperationOperationId as Async2Rule, | ||
| 'security-defined': SecurityDefined, |
There was a problem hiding this comment.
Please add support for AsyncAPI 3 as well. Right now the rule only applies to AsyncAPI2
There was a problem hiding this comment.
The code uses the rule name security-defined, but the docs still say asyncapi-operation-security-defined
Can you update related changes?
| @@ -0,0 +1,83 @@ | |||
| # asyncapi-operation-security-defined | |||
There was a problem hiding this comment.
Please don’t add this rule to the v1 docs
Performance Benchmark (Lower is Faster)
|
|
Hey @DmitryAnansky , I have fixed all comments and failing e2e tests and PR is good to review again. Thanks ! |
| ); | ||
| }); | ||
|
|
||
| it('should call createTestContext for arazzo type using the spec form without `workflows` segment', async () => { |
There was a problem hiding this comment.
Why do we need those changes?
There was a problem hiding this comment.
I missed the existing check which was already present hence I agree with you and I should remove this.
|
Hey @Daryna-del , I have addressed all the comments left by you and cursor and also resolved the failing e2e tests. Can you review the recent changes again ? Thanks ! |
| location: itemLocation, | ||
| name, | ||
| refPointer, | ||
| resolved: resolved.node !== undefined, |
There was a problem hiding this comment.
This check treats a null-valued scheme entry as defined, am I right?
Seems that a scheme key with no value (half-finished YAML) silently passes.
There was a problem hiding this comment.
I guess you are correct and I didnt catch it. $ref to a key with a null value resolves to node === null and since null !== undefined was marked as defined and silently passed.
| operation: SecuredOperation | undefined, | ||
| resolve: UserContext['resolve'] | ||
| ): boolean { | ||
| if (operation?.security) return true; |
There was a problem hiding this comment.
This condition might be confusing.
Seems the function consider empty security (e.g. security: []) as Secured operation.
But it is actually public.
And have similar check that will result in opposite verdict:
serverHasSecurity.set(
key.toString(),
Array.isArray(server?.security) && server.security.length > 0
);
| if (rootOperations && rootLocation) { | ||
| const operationsLocation = rootLocation.child(['operations']); | ||
| for (const [opName, opRef] of Object.entries(rootOperations)) { | ||
| const operation = isRef(opRef) ? resolve<Async3Operation>(opRef).node : opRef; |
There was a problem hiding this comment.
Looks like only refs against the root document will be resolved here.
Please double check, but it seems that refs recorded relative to another file never resolve, producing false 'Every operation should have security defined' errors on multi-file docs.
Please check context.resolve functionality, maybe you need to handle this case as well.
| return { | ||
| Root: { | ||
| enter(root, { location }: UserContext) { | ||
| rootServers = root?.servers; |
There was a problem hiding this comment.
Could you please check if you handle the scenario with two files correctly:
# main.yaml
asyncapi: 3.0.0
info:
title: Test
version: 1.0.0
servers:
$ref: './servers.yaml' # ← map-level ref
channels:
myChannel:
address: user/signedup
operations:
sendUserSignedUp:
action: send
channel:
$ref: '#/channels/myChannel'
# servers.yaml — the server IS secured
prod:
host: example.com
protocol: kafka
security:
- type: apiKey
in: user
|
|
||
| function pointsToSecurityScheme(pointer: string): boolean { | ||
| return ( | ||
| pointer.startsWith(SECURITY_SCHEMES_POINTER) && |
There was a problem hiding this comment.
Seems external file references are not handled here.
The AsyncAPI 3.0 specification defines security requirements as an array of Security Scheme Objects, and states the general rule: any time a Schema Object can be used, a Reference Object can be used in its place, allowing referencing definitions instead of defining them inline. The Reference Object itself uses JSON Reference resolution — "For this specification, reference resolution is done as defined by the JSON Reference specification and not by the JSON Schema specification." JSON Reference allows the $ref value to be any URI, which includes relative file paths and remote URLs.
Please check the example. We able to bundle it with ref resolution but this new rule will produce false failure during file lint.
asyncapi: 3.0.0
info:
title: Async3 External Security Ref
version: 1.0.0
servers:
production:
host: broker.example.com
protocol: mqtt
security:
- $ref: './apiKey.yaml'
channels:
userSignups:
address: user/signedup
messages:
userSignedUp:
payload:
type: object
properties:
name:
type: string
operations:
sendUserSignups:
action: send
channel:
$ref: '#/channels/userSignups'
security:
- $ref: './apiKey.yaml'
apiKey.yaml =>
type: httpApiKey
name: api_key
in: header
description: API key security scheme defined in an external fileThere was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ffc7ef8. Configure here.
| location: itemLocation, | ||
| name, | ||
| refPointer, | ||
| resolved: resolved.node !== undefined, |
There was a problem hiding this comment.
Null scheme counts as defined
Medium Severity
AsyncAPI 3 $ref resolution treats a target node of null as defined because resolved uses resolved.node !== undefined. AsyncAPI 2 marks any visited SecurityScheme key as defined without checking the scheme body, so a null entry under components.securitySchemes also passes.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ffc7ef8. Configure here.
| enter(root, { location }: UserContext) { | ||
| rootServers = root?.servers; | ||
| rootOperations = root?.operations; | ||
| rootLocation = location; |
There was a problem hiding this comment.
Map-level servers ref ignored
High Severity
When top-level servers is a single $ref to another file (instead of a named map), AsyncAPI 3 keeps the raw ref in rootServers and later treats the resolved value as one server object. AsyncAPI 2 never records those servers in serverHasSecurity, so applicability checks see no secured servers.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ffc7ef8. Configure here.


What/Why/How?
asyncapi-operation-security-definedrule for AsyncAPI 2.x and 3.x. both which reports when a security scheme referenced from an operation or serversecurityarray is not defined in ``components.securitySchemes.Reference
#2667
Testing
Screenshots (optional)
Check yourself
Security
Note
Medium Risk
New default
errorin recommended rulesets can break previously clean AsyncAPI lint runs; security and server-binding logic is nuanced and could produce false positives or misses on edge cases.Overview
Adds the
security-definedbuilt-in rule for AsyncAPI 2.x and 3.x, aligned with the existing OpenAPI rule name and behavior.For 2.x, the rule validates scheme names on operation/server
securityagainstcomponents.securitySchemes, and flags operations with no ownsecuritywhen applicable servers (including channel bindings and traits) do not fully cover them. For 3.x, it validates$reftargets undercomponents.securitySchemesand applies similar operation/server coverage checks on rootoperations.The rule is wired into async2/async3 rule registries, built-in rule IDs, and preset configs (
recommendedat error,minimalat warn). Docs, sidebar, changeset, and broad unit tests are included.Supporting changes add
isAsyncOperationSecured, richer AsyncAPI typings/visitors for security-related nodes, and minor AsyncAPI split typing refactors (AnyAsyncApiComponents).Reviewed by Cursor Bugbot for commit ffc7ef8. Bugbot is set up for automated code reviews on this repo. Configure here.