Skip to content

WS2001 crashes with 'dict_node' object has no attribute 'strip' when AccessLogSetting.Format / AccessLogSettings.Format is !Ref #253

@neboduus

Description

@neboduus

What were you trying to accomplish?

We run cfn-lint with serverless rules (-a cfn_lint_serverless.rules) in CI on SAM templates. Our API Gateway stages set access log format via a parameter: Format: !Ref ApiAccessLogFormat. The linter crashes with E0002 instead of either passing or reporting a normal WS2001 finding.


Expected Behavior

WS2001 (API Gateway Structured Logging) should either:

  • Report a warning if the format is not valid structured JSON, or
  • Skip / handle the case when Format is an intrinsic (e.g. !Ref) without raising an exception.

No uncaught exception and no E0002.


Current Behavior

When a template has AccessLogSetting.Format (REST) or AccessLogSettings.Format (HTTP) set to !Ref SomeParameter, rule WS2001 raises an AttributeError and cfn-lint reports E0002 (“Unknown exception while processing rule WS2001”) with:

'dict_node' object has no attribute 'strip'

Root cause: In cfn-lint-serverless/cfn_lint_serverless/rules/api_gateway.py, ApiGatewayStructuredLoggingRule.match() reads Format from the template (lines 104 / 106). When the template has Format: !Ref ..., that value is a dict-like node (e.g. {"Ref": "ApiAccessLogFormat"}), not a string. It is then passed to _check_log_format(log_format), which at line 80 does log_format = log_format.strip() without checking the type. Calling .strip() on that node causes the crash.


Possible Solution

In _check_log_format, only call .strip() when the value is actually a string. For example, at the start of _check_log_format:

  • If log_format is not a string (e.g. it’s a dict/node, as with !Ref / !Sub), return True (skip validation) or treat it as “not structured logging” and return False without calling .strip(), so no exception is raised.

Example guard:

def _check_log_format(self, log_format: str) -> bool:
    if not isinstance(log_format, str):
        return True  # skip validation when Format is intrinsic (Ref/Sub/etc.)
    log_format = log_format.strip()
    # ... rest unchanged

Alternatively, the rule could explicitly detect Ref/Sub nodes (e.g. via cfn-lint helpers) and skip or handle them before calling _check_log_format.


Steps to Reproduce (for bugs)

  1. Install: pip install cfn-lint cfn-lint-serverless (cfn-lint 1.44.0).
  2. Save the following as template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Reproduce WS2001 crash when Format is !Ref

Parameters:
  LogFormat:
    Type: String
    Default: '{"requestId":"$context.requestId"}'

Resources:
  Api:
    Type: AWS::Serverless::Api
    Properties:
      StageName: prod
      AccessLogSetting:
        DestinationArn: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/apigateway'
        Format: !Ref LogFormat
  1. Run: cfn-lint template.yaml -a cfn_lint_serverless.rules
  2. Observe E0002 with message 'dict_node' object has no attribute 'strip'.

Environment

  • Infrastructure as code technology used: AWS SAM (CloudFormation + Serverless transform), YAML templates.
  • (for cfn-lint) Python, cfn-lint, and cfn-lint-serverless versions: Python 3.x, cfn-lint 1.44.0, cfn-lint-serverless (from PyPI: 0.3.5).
  • Debugging logs
$ cfn-lint template.yaml -a cfn_lint_serverless.rules
E0002 Unknown exception while processing rule WS2001: 'dict_node' object has no attribute 'strip'
template.yaml:19:7

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions