From a444022cc8f0325ff0141431052c8c38cb661420 Mon Sep 17 00:00:00 2001 From: Seppli11 Date: Mon, 23 Jun 2025 14:27:16 +0000 Subject: [PATCH 1/2] Create rule S7613 --- rules/S7613/metadata.json | 2 ++ rules/S7613/python/metadata.json | 25 ++++++++++++++++++ rules/S7613/python/rule.adoc | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 rules/S7613/metadata.json create mode 100644 rules/S7613/python/metadata.json create mode 100644 rules/S7613/python/rule.adoc diff --git a/rules/S7613/metadata.json b/rules/S7613/metadata.json new file mode 100644 index 00000000000..2c63c085104 --- /dev/null +++ b/rules/S7613/metadata.json @@ -0,0 +1,2 @@ +{ +} diff --git a/rules/S7613/python/metadata.json b/rules/S7613/python/metadata.json new file mode 100644 index 00000000000..eecef062eed --- /dev/null +++ b/rules/S7613/python/metadata.json @@ -0,0 +1,25 @@ +{ + "title": "FIXME", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-7613", + "sqKey": "S7613", + "scope": "All", + "defaultQualityProfiles": ["Sonar way"], + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH", + "RELIABILITY": "MEDIUM", + "SECURITY": "LOW" + }, + "attribute": "CONVENTIONAL" + } +} diff --git a/rules/S7613/python/rule.adoc b/rules/S7613/python/rule.adoc new file mode 100644 index 00000000000..caae0d69054 --- /dev/null +++ b/rules/S7613/python/rule.adoc @@ -0,0 +1,44 @@ +FIXME: add a description + +// If you want to factorize the description uncomment the following line and create the file. +//include::../description.adoc[] + +== Why is this an issue? + +FIXME: remove the unused optional headers (that are commented out) + +//=== What is the potential impact? + +== How to fix it +//== How to fix it in FRAMEWORK NAME + +=== Code examples + +==== Noncompliant code example + +[source,python,diff-id=1,diff-type=noncompliant] +---- +FIXME +---- + +==== Compliant solution + +[source,python,diff-id=1,diff-type=compliant] +---- +FIXME +---- + +//=== How does this work? + +//=== Pitfalls + +//=== Going the extra mile + + +//== Resources +//=== Documentation +//=== Articles & blog posts +//=== Conference presentations +//=== Standards +//=== External coding guidelines +//=== Benchmarks From b27e81b8320a7b012a826cef3753f063b246d612 Mon Sep 17 00:00:00 2001 From: Sebastian Zumbrunn Date: Mon, 23 Jun 2025 17:36:13 +0200 Subject: [PATCH 2/2] SONARPY-3057: Returned Values Must Be JSON Serializable --- rules/S7613/python/metadata.json | 11 ++- rules/S7613/python/rule.adoc | 120 +++++++++++++++++++++++++------ 2 files changed, 105 insertions(+), 26 deletions(-) diff --git a/rules/S7613/python/metadata.json b/rules/S7613/python/metadata.json index eecef062eed..3345d9ebd69 100644 --- a/rules/S7613/python/metadata.json +++ b/rules/S7613/python/metadata.json @@ -1,6 +1,6 @@ { - "title": "FIXME", - "type": "CODE_SMELL", + "title": "AWS Lambda handlers should return only JSON serializable values", + "type": "BUG", "status": "ready", "remediation": { "func": "Constant\/Issue", @@ -13,12 +13,11 @@ "sqKey": "S7613", "scope": "All", "defaultQualityProfiles": ["Sonar way"], - "quickfix": "unknown", + "quickfix": "infeasible", "code": { "impacts": { - "MAINTAINABILITY": "HIGH", - "RELIABILITY": "MEDIUM", - "SECURITY": "LOW" + "MAINTAINABILITY": "LOW", + "RELIABILITY": "HIGH" }, "attribute": "CONVENTIONAL" } diff --git a/rules/S7613/python/rule.adoc b/rules/S7613/python/rule.adoc index caae0d69054..abc90c4e4f6 100644 --- a/rules/S7613/python/rule.adoc +++ b/rules/S7613/python/rule.adoc @@ -1,16 +1,46 @@ -FIXME: add a description - -// If you want to factorize the description uncomment the following line and create the file. -//include::../description.adoc[] +This rule raises an issue when AWS Lambda handlers return values that are not JSON serializable. == Why is this an issue? -FIXME: remove the unused optional headers (that are commented out) - -//=== What is the potential impact? +For synchronous AWS Lambda invocations, like via API Gateway or direct SDK calls, the value returned by the handler is automatically serialized into a JSON string before being sent back in the response. If the return value contains objects that are not native JSON types like `datetime` objects, sets, or custom class instances, the serialization will fail, causing a `TypeError`. This will prevent the Lambda from returning a valid response to the client. == How to fix it -//== How to fix it in FRAMEWORK NAME + +Convert non-JSON-serializable objects to their string representation or to native JSON types before returning them: + +* For `datetime` objects: Convert to ISO format strings using `.isoformat()` +* For sets: Convert to lists using `list(set_object)` +* For custom objects: convert them to dictionaries using the `++__dict__++` field, `dataclasses.asdict(...)` for dataclasses or a custom `todict()` method. + +For more control over serialization, return a string instead and handle the serialization logic yourself. + +Here are examples of serializing a custom class using different methods: + +[source,python] +---- +import json +import dataclasses + +# A custom class representing a user +@dataclasses.dataclass +class User: + name: str + age: int + + def to_dict(self) -> dict: + return { "name": self.name, "age": self.age } + +user = User("Alice", 30) + +# Method 1: Using __dict__ field +json.dumps(user.__dict__) + +# Method 2: Using dataclasses.asdict() +json.dumps(dataclasses.asdict(user)) + +# Method 3: Using custom to_dict() method +json.dumps(user.to_dict()) +---- === Code examples @@ -18,27 +48,77 @@ FIXME: remove the unused optional headers (that are commented out) [source,python,diff-id=1,diff-type=noncompliant] ---- -FIXME +import datetime + +def lambda_handler(event, context): + return { + "message": "Request processed successfully", + "timestamp": datetime.datetime.now() # Noncompliant: not JSON serializable + } ---- ==== Compliant solution [source,python,diff-id=1,diff-type=compliant] ---- -FIXME +import datetime + +def lambda_handler(event, context): + return { + "message": "Request processed successfully", + "timestamp": datetime.datetime.now().isoformat() # Compliant: converted to string + } ---- -//=== How does this work? -//=== Pitfalls +== Resources + +=== Documentation + +* AWS Documentation - https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html#python-handler-return[Define Lambda function handler in Python +] +* Python Documentation - https://docs.python.org/3/library/json.html[json — JSON encoder and decoder] +* Python Documentation - https://docs.python.org/3/reference/datamodel.html#object.__dict__[object.++__dict__++ field] +* Python Documentation - https://docs.python.org/3/library/datetime.html#datetime.date.isoformat[datetime.date.isoformat()] +* Python Documentation - https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict[dateclasses.asdict()] + +ifdef::env-github,rspecator-view[] +== Implementation Specification +visible only on this page + +The implementation should check for these common non-serializable types: +* `datetime.datetime`, `datetime.date`, `datetime.time` +* `set`, `frozenset` +* `bytes`, `bytearray` +* `complex` numbers +* Custom class instances without proper serialization methods +* `decimal.Decimal` objects +* File objects +* Functions and methods +* Module objects +* `re.Pattern` objects + +Since business logic and the handler should be separated, it is possible that the lambda handler as the last statement class a function and returns its result. If this is detect, it might be worth also checking the return type of the function. + +Furthermore, it is common that the lambda handler has early returns which return a different JSON in case an error occurs. In this case, the rule should also check the return type of the handler in all branches. + +[source,python] +---- +def process_event(event): + if not event.get("valid"): + return {"error": "Invalid event"} + + result = {"status": "success", "data": event["data"]} + return result + +def lambda_handler(event, context): + return process_event(event) +---- -//=== Going the extra mile +=== Message +Lambda handler returns a value that is not JSON serializable. +=== Highlighting +The non-serializable value or object in the return statement. -//== Resources -//=== Documentation -//=== Articles & blog posts -//=== Conference presentations -//=== Standards -//=== External coding guidelines -//=== Benchmarks +endif::env-github,rspecator-view[]