Fix security attrs parse#2199
Conversation
|
In a slightly more comprehensive test, it was shown that the problem lies after the security attributes. For example, let's consider the test for this package main
// @title Some API Title
// @version 0.0.1
// @BasePath /
// @schemes http https
//
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Type "Bearer" followed by a space and JWT token.
//
// @tag.name dogs
// @tag.description Dogs are cool
//
// @tag.name cats
// @tag.description Cats are the devil
// @tag.docs.url https://example.com/cats
// @tag.docs.description Everything about cats
//
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
//
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
//
// @termsOfService http://swagger.io/terms/
//
// @security Bearer
//
// @externalDocs.description Some description here
// @externalDocs.url http://some.url.com/path/here/
//
// @x-foo "some value"
func main() {}When running the following test, all conditions fail: func TestApiParse_R(t *testing.T) {
t.Parallel()
searchDir := "testdata/tags3"
p := New()
p.PropNamingStrategy = PascalCase
err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth)
assert.NoError(t, err)
assert.Equal(t, "API Support", p.swagger.Info.Contact.Name)
assert.Equal(t, "http://www.swagger.io/support", p.swagger.Info.Contact.URL)
assert.Equal(t, "support@swagger.io", p.swagger.Info.Contact.Email)
if assert.NotNil(t, p.swagger.Info.License) {
assert.Equal(t, "Apache 2.0", p.swagger.Info.License.Name)
assert.Equal(t, "http://www.apache.org/licenses/LICENSE-2.0.html", p.swagger.Info.License.URL)
}
assert.Equal(t, "http://swagger.io/terms/", p.swagger.Info.TermsOfService)
assert.Len(t, p.swagger.Security, 1)
if assert.NotNil(t, p.swagger.ExternalDocs) {
assert.Equal(t, "Some description here", p.swagger.ExternalDocs.Description)
assert.Equal(t, "http://some.url.com/path/here/", p.swagger.ExternalDocs.URL)
}
// @x-foo is a top-level extension; it must not end up attached to the
// Bearer security scheme instead.
topLevel, onDoc := p.swagger.Extensions.GetString("x-foo")
assert.True(t, onDoc, "@x-foo missing from the top-level document")
assert.Equal(t, "some value", topLevel)
_, onScheme := p.swagger.SecurityDefinitions["Bearer"].Extensions.GetString("x-foo")
assert.False(t, onScheme, "@x-foo ended up attached to the Bearer security scheme instead of the top-level document")
assert.Len(t, p.swagger.Tags, 2)
dogs := p.swagger.Tags[0]
assert.Equal(t, "dogs", dogs.TagProps.Name)
assert.Equal(t, "Dogs are cool", dogs.TagProps.Description)
cats := p.swagger.Tags[1]
assert.Equal(t, "cats", cats.TagProps.Name)
assert.Equal(t, "Cats are the devil", cats.TagProps.Description)
if assert.NotNil(t, cats.TagProps.ExternalDocs) {
assert.Equal(t, "https://example.com/cats", cats.TagProps.ExternalDocs.URL)
assert.Equal(t, "Everything about cats", cats.TagProps.ExternalDocs.Description)
}
}Test output: But if we move |
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| - | - | Generic Password | fe9cfcf | parser_test.go | View secret |
| - | - | Generic Password | 7119635 | parser_test.go | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
|
a false positive 👆🏻 |
|
I tried changing a few values, but unfortunately it didn't work. |
Hey folks.
Recently, I was working on API documentation and noticed that the information from the tags I was defining (
@tag.nameand@tag.description) wasn't being included in the documentation generated by swag CLI. When I searched for issues with the same or a similar problem, I found #1497 and then began to investigate the issue further.While trying to reproduce the issue in tests, I realized that the problem wasn't with the flags passed to the CLI (such as
--parseDependencyor--dir), but rather with the order in which the information was presented in the function's documentation. In this case, when the security attributes come before the tag attributes.@tag.name/@tag.descriptionannotations are silently dropped from the generated Swagger docs when a@securityDefinitions.*block appears earlier in the same general API info comment group. No error or warning is produced —swagger.Tagssimply comes back empty.To Reproduce
Steps to reproduce the behavior:
swag initagainst it.swagger.json/swagger.yamland look at thetagskey: it's an empty array, even thoughdogsandcatsare both declared with descriptions right there in the source.Test Setup
Added
testdata/tags3/main.go, trimmed to just what's needed to isolate the bug: a general API info block with@title/@version/@BasePath, one@securityDefinitions.apikeyscheme, two@tag.name/@tag.descriptionpairs (dogs,cats) — trying to reflect as closely as possible the structure of a typical real-worldmain.gofile.Added
TestApiParseTag_AfterSecurityDefinitionsinparser_test.go(parser_test.go:3136), right afterTestParseTagMarkdownDescription:Before the fix, this test failed because the list of tags was empty (
p.swagger.Tags).It is important to note that if the tags come BEFORE the security attributes, the test passes correctly without any changes to the code.
Solution
Root cause is in
parseSecAttributes(parser.go:764), the function that parses one@securityDefinitions.*block. It's called from the general-info parser's outer loop with the loop's own line index, passed by pointer:parseSecAttributesthen runs its own scan (thelooplineloop, parser.go:795-844) over that same shared slice, advancing the shared index while looking for the attributes that belong to that security scheme (@in,@name,@tokenUrl,@authorizationUrl,@scope.*,@description,@x-*). The only thing that stopped the scan was running into another@securityDefinitions.*line. Any other, unrelated attribute —@tag.name,@tag.description,@contact.*,@license.*,@externalDocs.*— fell through unrecognized and was silently skipped, with the shared index advancing right past it. With no second security block to act as a stop sign, the scan ran to the end of the comment group and quietly consumed everything after it, tags included, before the outer general-info loop ever got to see those lines.The fix has two parts, both inside that same
looplineloop:@securityDefinitions.*line — back the shared index up one line and break, handing control back to the outer general-info loop so it can process that line itself (where@tag.name,@contact.name, etc. are already handled correctly).continueto the existing@descriptionbranch. This surfaced while implementing (1): the@descriptionbranch never had acontinue, relying on the old code having nothing after it besides the "next security block" check. Once (1) added a fallback after that check, a line that had already been handled by the@descriptionbranch fell through into the new fallback too, ending the scan one line early.diff for the suggested change:
// Not mandatory field if securityAttr == descriptionAttr { if description != "" { description += "\n" } description += value + continue } - // next securityDefinitions - if strings.Index(securityAttr, "@securitydefinitions.") == 0 { - // Go back to the previous line and break - *index-- - - break - } + // Either a new @securityDefinitions block starts here, or the attribute + // is unrecognized and belongs to the outer general-info parser. + // In both cases, go back to the previous line and stop. + *index-- + break }The change is minimal and contained to
parseSecAttributes. Verified withmake test: the full suite across theswag,gen, andformatpackages passes, including the new regression test and the two tests that an earlier, incomplete version of this fix had broken.Expected behavior
@tag.name/@tag.descriptionpairs are parsed intoswagger.Tagsregardless of what other general-info annotations — security definitions, contact, license, external docs — precede them in the same comment block.Local setup information: