Must HTTP Status Codes be strings? - openapi

The Responses object contains a {HTTP Status Code: Response} mapping.
In all the examples I found, the status code is provided as a string:
{"200": {"description": "a pet to be returned"}}
I couldn't find any requirement for it to be a string and integers are accepted by the validators I tried.
All I found was a PR changing from integer to string in all YAML examples.
Should I only use strings?
Edit: In JSON, only strings are valid keys. So the question could be rephrased as "which of the following two assumptions is correct"?
OpenAPI doesn't specify that HTTP Status Codes should be strings because that's implicit (JSON format). However, validation and display tools are being loose about that requirement.
OpenAPI uses some kind of "JSON superset" in which integer keys are considered valid.

From this GH issue, the keys must be strings:
OpenAPI can be represented canonically in either JSON or YAML, as you say in JSON only strings can be keys. With regard to YAML:
This field MUST be enclosed in quotation marks (for example, "200") for compatibility between JSON and YAML.
This has the effect that the key is always a string type.
This is not really a specification, but rather a requirement of the JSON format.

Related

RESTCONF/Yangdata endpoints not working properly with URL encoding

Lets take this URL for example
https://<ip>/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1
If i send a request to a similar endpoint using the internal library we've been using the server doesn't understand it since the = is encoded to %3D but making the same request on the CLI with curl works fine.
I'm not understanding why it's an issue, isn't = supposed to be encoded in a URL anyway? Is it something to do with the library treating it as a query?
The = represents a special character with special meaning in a RESTCONF URI. If you encode it as %3D, you take that special meaning away and reduce it to just a character. The only case where you would do that is when a list's key value or a leaf-list's value contain this character and you are referencing such an instance in your URI.
You are only allowed to use = when referencing list and leaf-list instances - and you do not percent encode it when you do use it.
In your case, GigabitEthernet would be expected to represent a list/leaf-list, with its key/type accepting 1 as a valid value. Both native and interface would be (and can only be) containers (or anydata).
RFC8040, Section 3.5.3, Encoding Data Resource Identifiers in the Request URI describes this in detail.
Here are the examples from the same section:
Examples:
container top {
list list1 {
key "key1 key2 key3";
...
list list2 {
key "key4 key5";
...
leaf X { type string; }
}
}
leaf-list Y {
type uint32;
}
}
For the above YANG definition, the container "top" is defined in the
"example-top" YANG module, and a target resource URI for leaf "X"
would be encoded as follows:
/restconf/data/example-top:top/list1=key1,key2,key3/list2=key4,key5/X
For the above YANG definition, a target resource URI for
leaf-list "Y" would be encoded as follows:
/restconf/data/example-top:top/Y=instance-value
The following example shows how reserved characters are
percent-encoded within a key value. The value of "key1" contains
a comma, single-quote, double-quote, colon, double-quote, space,
and forward slash (,'":" /). Note that double-quote is not a
reserved character and does not need to be percent-encoded. The
value of "key2" is the empty string, and the value of "key3" is the
string "foo".
Example URL:
/restconf/data/example-top:top/list1=%2C%27"%3A"%20%2F,,foo
Note the last example in particular. You would need to percent encode a =, if it appears after list1= in its key value.
Note that also that these are not parts of a query - they are path segments (RFC3986 terms).

In OpenAPI 3.0, should I specify maxLength for a string with a specified format?

I have some string parameters with specified format in my OpenAPI documentation.
email:
type: string
format: email
hostname:
type: string
format: hostname
path:
type: string
format: uri
I want to define maxLength to protect from harmful queries.
Do I have to do it or does format already define the maximum length?
For some of the formats the length of its value is defined. You can refer https://github.com/OAI/OpenAPI-Specification/issues/607#issue-142290879 to get the RFC definition for these formats. Apart from those if you think you need to have your predefined max/min length for the string value you can add them or you can use pattern keyword as well if you want to introduce any custom formats in your API definition. Using format has its own advantage and disadvantages.
Advantage
You can keep the API definition clean with few lines
You don't have to worry about the length if you want to follow a generally accepted lengths for your payload parameters.
Disadvantage
You have to stick to a predefined format
If you are not aware of the format details then your requests/responses might get failed.

Requests fail authorization when query string contains certain characters

I'm making requests to Twitter, using the OAuth1.0 signing process to set the Authorization header. They explain it step-by-step here, which I've followed. It all works, most of the time.
Authorization fails whenever special characters are sent without percent encoding in the query component of the request. For example, ?status=hello%20world! fails, but ?status=hello%20world%21 succeeds. But the change from ! to the percent encoded form %21 is only made in the URL, after the signature is generated.
So I'm confused as to why this fails, because AFAIK that's a legally encoded query string. Only the raw strings ("status", "hello world!") are used for signature generation, and I'd assume the server would remove any percent encoding from the query params and generate its own signature for comparison.
When it comes to building the URL, I let URLComponents do the work, so I don't add percent encoding manually, ex.
var urlComps = URLComponents()
urlComps.scheme = "https"
urlComps.host = host
urlComps.path = path
urlComps.queryItems = [URLQueryItem(key: "status", value: "hello world!")]
urlComps.percentEncodedQuery // "status=hello%20world!"
I wanted to see how Postman handled the same request. I selected OAuth1.0 as the Auth type and plugged in the same credentials. The request succeeded. I checked the Postman console and saw ?status=hello%20world%21; it was percent encoding the !. I updated Postman, because a nice little prompt asked me to. Then I tried the same request; now it was getting an authorization failure, and I saw ?status=hello%20world! in the console; the ! was no longer being percent encoded.
I'm wondering who is at fault here. Perhaps Postman and I are making the same mistake. Perhaps it's with Twitter. Or perhaps there's some proxy along the way that idk, double encodes my !.
The OAuth1.0 spec says this, which I believe is in the context of both client (taking a request that's ready to go and signing it before it's sent), and server (for generating another signature to compare against the one received):
The parameters from the following sources are collected into a
single list of name/value pairs:
The query component of the HTTP request URI as defined by
[RFC3986], Section 3.4. The query component is parsed into a list
of name/value pairs by treating it as an
"application/x-www-form-urlencoded" string, separating the names
and values and decoding them as defined by
[W3C.REC-html40-19980424], Section 17.13.4.
That last reference, here, outlines the encoding for application/x-www-form-urlencoded, and says that space characters should be replaced with +, non-alphanumeric characters should be percent encoded, name separated from value by =, and pairs separated by &.
So, the OAuth1.0 spec says that the query string of the URL needs to be decoded as defined by application/x-www-form-urlencoded. Does that mean that our query string needs to be encoded this way too?
It seems to me, if a request is to be signed using OAuth1.0, the query component of the URL that gets sent must be encoded in a way that is different to what it would normally be encoded in? That's a pretty significant detail if you ask me. And I haven't seen it explicitly mentioned, even in Twitter's documentation. And evidently the folks at Postman overlooked it too? Unless I'm not supposed to be using URLComponents to build a URL, but that's what it's for, no? Have I understood this correctly?
Note: ?status=hello+world%21 succeeds; it tweets "hello world!"
I ran into a similar issue.
put the status in post body, not query string.
Percent-encoding:
private encode(str: string) {
// encodeURIComponent() escapes all characters except: A-Z a-z 0-9 - _ . ! ~ * " ( )
// RFC 3986 section 2.3 Unreserved Characters (January 2005): A-Z a-z 0-9 - _ . ~
return encodeURIComponent(str)
.replace(/[!'()*]/g, c => "%" + c.charCodeAt(0).toString(16).toUpperCase());
}

Spring cloud gateway uri decoding failing

I wrote a gateway application using Spring cloud Greenwich binaries. I'm seeing issues when special characters are present in URL. The request fails with below exception in Spring gateway when request URI contains special characters.
localhost:8080/myresource/WG_splchar_%26%5E%26%25%5E%26%23%25%24%5E%26%25%26*%25%2B)!%24%23%24%25%26%5E_new
When I hit above url, Spring fails with below exception. I'm not able to figure out why it's an invalid sequence and how things like these can be handled.
java.lang.IllegalArgumentException: Invalid encoded sequence "%^&#%$^&%&*%+)!$#$%&^_new"
at org.springframework.util.StringUtils.uriDecode(StringUtils.java:741) ~[spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.http.server.DefaultPathContainer.parsePathSegment(DefaultPathContainer.java:126) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.http.server.DefaultPathContainer.createFromUrlPath(DefaultPathContainer.java:111) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.http.server.PathContainer.parsePath(PathContainer.java:76) ~[spring-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory.lambda$apply$2(PathRoutePredicateFactory.java:79) ~[spring-cloud-gateway-core-2.1.0.RC3.jar:2.1.0.RC3]
at org.springframework.cloud.gateway.support.ServerWebExchangeUtils.lambda$toAsyncPredicate$1(ServerWebExchangeUtils.java:128) ~[spring-cloud-gateway-core-2.1.0.RC3.jar:2.1.0.RC3]
at org.springframework.cloud.gateway.handler.AsyncPredicate.lambda$and$1(AsyncPredicate.java:35) ~[spring-cloud-gateway-core-2.1.0.RC3.jar:2.1.0.RC3]
at org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping.lambda$null$2(RoutePredicateHandlerMapping.java:112) ~[spring-cloud-gateway-core-2.1.0.RC3.jar:2.1.0.RC3]
at reactor.core.publisher.MonoFilterWhen$MonoFilterWhenMain.onNext(MonoFilterWhen.java:116) [reactor-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2070) [reactor-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at reactor.core.publisher.MonoFilterWhen$MonoFilterWhenMain.onSubscribe(MonoFilterWhen.java:103) [reactor-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) [reactor-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at reactor.core.publisher.MonoFilterWhen.subscribe(MonoFilterWhen.java:56) [reactor-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
I answered the other question already and don't feel like retyping. The spirit of the answer is the exact same.
Write a unit test exercising this method off of the Spring cloud utils. This is what's breaking. You can try passing in more or less of the string you're concerned about to find where the breakage is. Use a binary search to figure out what's broken. Make sure you don't split the string in the middle of an encoded character or else you'll give yourself a false positive. When it says you have an invalid sequence I would expect you have something like %99 where 99 is does not map to any valid character (I'm just making one up)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/StringUtils.html#uriDecode-java.lang.String-java.nio.charset.Charset-
As an aside
Where is this encoded string coming from? Did someone at your company create their own solution to encode this string to begin with? Are you accepting user data? It's VERY POSSIBLE that whomever is responsible for producing this string encoded it incorrectly by homerolling their own encoder.
ALTERNATIVELY
spring.cloud.gateway.routes[7].predicates[0]=Path=/test/{testId}/test1/test_%26%5E%26%25%5E%26%25%26*%25%2B)!
When I look at this I see a path that is already encoded. For example, you've taken your ampersand & character and replaced it with %26
Have you tried inputting a path that is NOT already encoded?
For example
spring.cloud.gateway.routes[7].predicates[0]=Path=/test/{testId}/test1/test_&^&%^ < I only partially decoded it by hand using this chart. https://www.w3schools.com/tags/ref_urlencode.asp

Can Integer in URL be typecasted to String in Query Param?

For the request
http://xyz/resource?articleid=232&name=John
Response getDetails(#QueryParam("articleid") String articleid,(#QueryParam("name") String name){}
Is the above Query Parameter correct for the given URL?
Executive summary: there is no "integer" in the URL; there's only a string. The implementation is does extra work to convert the string into an integer if you ask it to do so.
Is the above Query Parameter correct for the given URL?
That should be perfectly acceptable.
https://www.rfc-editor.org/rfc/rfc3986#section-3.4
query is a just a sequence of pchar (plus '/' and '?'), which is to say it's just data.
A query in that form is usually an expression of an application/x-www-form-urlencoded resource. The key hint in the specification is
Let output be an initially empty list of name-value tuples where both name and value hold a string.
The JAX-RS specification describes the transformation of these string to other types, but it defers to the java doc for the annotation. Of course, QueryParam is in close alignment with the specification, so both places give the same answer.