mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 13:07:02 +00:00
Handling non escaped strings in swagger generator
This commit is contained in:
parent
00490b95ed
commit
72ad27d2b8
@ -40,6 +40,35 @@ import scala.reflect.runtime.universe
|
||||
|
||||
object SwaggerJSONFactory extends MdcLoggable {
|
||||
type Coll[T] = GenTraversableLike[T, _]
|
||||
|
||||
/**
|
||||
* Escapes a string value to be safely included in JSON.
|
||||
* Handles quotes, backslashes, newlines, and other special characters.
|
||||
*/
|
||||
private def escapeJsonString(value: String): String = {
|
||||
if (value == null) return ""
|
||||
value
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\r", "\\r")
|
||||
.replace("\t", "\\t")
|
||||
.replace("\b", "\\b")
|
||||
.replace("\f", "\\f")
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts any value to a JSON example string.
|
||||
* Handles JValue, String, and other types with proper escaping.
|
||||
*/
|
||||
private def safeExampleValue(value: Any): String = {
|
||||
value match {
|
||||
case null | None => ""
|
||||
case v: JValue => try { escapeJsonString(JsonUtils.toString(v)) } catch { case e: Exception => logger.warn(s"Failed to convert JValue to string for example: ${e.getMessage}"); "" }
|
||||
case v: String => escapeJsonString(v)
|
||||
case v => escapeJsonString(v.toString)
|
||||
}
|
||||
}
|
||||
//Info Object
|
||||
//link ->https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#infoObject
|
||||
case class InfoJson(
|
||||
@ -107,14 +136,26 @@ object SwaggerJSONFactory extends MdcLoggable {
|
||||
| }
|
||||
|}
|
||||
|""".stripMargin
|
||||
json.parse(definition)
|
||||
try {
|
||||
json.parse(definition)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.error(s"Failed to parse ListResult schema JSON: ${e.getMessage}\nJSON was: $definition")
|
||||
throw new RuntimeException(s"Invalid JSON in ListResult schema generation: ${e.getMessage}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
case class JObjectSchemaJson(jObject: JObject) extends ResponseObjectSchemaJson with JsonAble {
|
||||
|
||||
override def toJValue(implicit format: Formats): json.JValue = {
|
||||
val schema = buildSwaggerSchema(typeOf[JObject], jObject)
|
||||
json.parse(schema)
|
||||
try {
|
||||
json.parse(schema)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.error(s"Failed to parse JObject schema JSON: ${e.getMessage}\nSchema was: $schema")
|
||||
throw new RuntimeException(s"Invalid JSON in JObject schema generation: ${e.getMessage}", e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -122,7 +163,13 @@ object SwaggerJSONFactory extends MdcLoggable {
|
||||
|
||||
override def toJValue(implicit format: Formats): json.JValue = {
|
||||
val schema = buildSwaggerSchema(typeOf[JArray], jArray)
|
||||
json.parse(schema)
|
||||
try {
|
||||
json.parse(schema)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.error(s"Failed to parse JArray schema JSON: ${e.getMessage}\nSchema was: $schema")
|
||||
throw new RuntimeException(s"Invalid JSON in JArray schema generation: ${e.getMessage}", e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -646,8 +693,7 @@ object SwaggerJSONFactory extends MdcLoggable {
|
||||
}
|
||||
def example = exampleValue match {
|
||||
case null | None => ""
|
||||
case v: JValue => s""", "example": "${JsonUtils.toString(v)}" """
|
||||
case v => s""", "example": "$v" """
|
||||
case v => s""", "example": "${safeExampleValue(v)}" """
|
||||
}
|
||||
|
||||
paramType match {
|
||||
@ -968,11 +1014,12 @@ object SwaggerJSONFactory extends MdcLoggable {
|
||||
.toList
|
||||
.map(it => {
|
||||
val (errorName, errorMessage) = it
|
||||
val escapedMessage = escapeJsonString(errorMessage.toString)
|
||||
s""""Error$errorName": {
|
||||
| "properties": {
|
||||
| "message": {
|
||||
| "type": "string",
|
||||
| "example": "$errorMessage"
|
||||
| "example": "$escapedMessage"
|
||||
| }
|
||||
| }
|
||||
}""".stripMargin
|
||||
@ -989,7 +1036,14 @@ object SwaggerJSONFactory extends MdcLoggable {
|
||||
//Make a final string
|
||||
val definitions = "{\"definitions\":{" + particularDefinitionsPart + "}}"
|
||||
//Make a jsonAST from a string
|
||||
parse(definitions)
|
||||
try {
|
||||
parse(definitions)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.error(s"Failed to parse Swagger definitions JSON: ${e.getMessage}")
|
||||
logger.error(s"JSON was: ${definitions.take(500)}...")
|
||||
throw new RuntimeException(s"Invalid JSON in Swagger definitions generation. This may be due to unescaped special characters in examples or field names. Error: ${e.getMessage}", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -99,4 +99,57 @@ class SwaggerFactoryUnitTest extends V140ServerSetup with MdcLoggable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
feature("Test JSON escaping robustness in Swagger generation") {
|
||||
scenario("Test quotes in example values are properly escaped") {
|
||||
case class TestWithQuotes(name: String, description: String)
|
||||
val testObj = TestWithQuotes(name = "Test with \"quotes\"", description = "Has 'single' and \"double\" quotes")
|
||||
val result = SwaggerJSONFactory.translateEntity(testObj)
|
||||
noException should be thrownBy { net.liftweb.json.parse("{" + result + "}") }
|
||||
result should include ("\\\"")
|
||||
}
|
||||
|
||||
scenario("Test newlines and special chars are properly escaped") {
|
||||
case class TestWithNewlines(text: String)
|
||||
val testObj = TestWithNewlines(text = "Line 1\nLine 2\tTab")
|
||||
val result = SwaggerJSONFactory.translateEntity(testObj)
|
||||
noException should be thrownBy { net.liftweb.json.parse("{" + result + "}") }
|
||||
result should include ("\\n")
|
||||
}
|
||||
|
||||
scenario("Test ABAC rule-like strings with escaped quotes") {
|
||||
case class AbacRule(rule: String)
|
||||
val testObj = AbacRule(rule = """user.emailAddress.contains(\"admin\")""")
|
||||
val result = SwaggerJSONFactory.translateEntity(testObj)
|
||||
noException should be thrownBy { net.liftweb.json.parse("{" + result + "}") }
|
||||
}
|
||||
|
||||
scenario("Test error messages with special characters") {
|
||||
import code.api.v1_4_0.JSONFactory1_4_0
|
||||
val mockResourceDoc = JSONFactory1_4_0.ResourceDocJson(
|
||||
operation_id = "testOp",
|
||||
implemented_by = JSONFactory1_4_0.ImplementedByJson("1.0.0", "test"),
|
||||
request_verb = "GET",
|
||||
request_url = "/test",
|
||||
summary = "Test",
|
||||
description = "Test desc",
|
||||
description_markdown = "Test desc",
|
||||
example_request_body = null,
|
||||
success_response_body = SwaggerDefinitionsJSON.bankJSON,
|
||||
error_response_bodies = List("OBP-10000"),
|
||||
tags = List("Test"),
|
||||
typed_request_body = net.liftweb.json.JNothing,
|
||||
typed_success_response_body = net.liftweb.json.JNothing,
|
||||
roles = Some(List()),
|
||||
is_featured = false,
|
||||
special_instructions = "",
|
||||
specified_url = "/obp/v4.0.0/test",
|
||||
connector_methods = List(),
|
||||
created_by_bank_id = None
|
||||
)
|
||||
noException should be thrownBy {
|
||||
SwaggerJSONFactory.loadDefinitions(List(mockResourceDoc), SwaggerDefinitionsJSON.allFields.take(10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user