awslabs / awslabs/serverless-rules

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

Open
#253 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
420
Forks
29
PR merge metrics
No merged PRs in 30d

Description

**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](https://github.com/awslabs/serverless-rules/blob/main/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:

```python
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`:

```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
```

3. Run: `cfn-lint template.yaml -a cfn_lint_serverless.rules`
4. 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
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.