Skip to content

Commit

Permalink
✨ Add XMLPublicID condition to builtin provider (#306)
Browse files Browse the repository at this point in the history
Adding XML-like public-id condition supporting regular expression
evaluation on the public-id attribute.

Related to konveyor/windup-shim#87

Fixes: #221

Analyzer PR: 306
Windup-shim PR: 87

---------

Signed-off-by: Marek Aufart <[email protected]>
Signed-off-by: Marek Aufart <[email protected]>
  • Loading branch information
aufi authored Dec 12, 2023
1 parent 1838b1d commit ca8ced3
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 32 deletions.
10 changes: 10 additions & 0 deletions demo-output.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,16 @@
variables:
name: junit.junit
version: "4.11"
jboss-eap5-7-xml-02000:
description: ""
category: potential
incidents:
- uri: file:///analyzer-lsp/examples/java/jboss-app.xml
message: JBoss 5.x EAR descriptor (jboss-app.xml) was found with public-id
variables:
data: module
innerText: "\n jboss-example-service\n "
matchingXML: <service>jboss-example-service</service>
lang-ref-001:
description: ""
category: potential
Expand Down
8 changes: 8 additions & 0 deletions examples/java/jboss-app.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<jboss-app>
<loader-repository>
</loader-repository>
<module public-id="JBoss DTD Java EE 5">
<service>jboss-example-service</service>
</module>
</jboss-app>
11 changes: 11 additions & 0 deletions provider/internal/builtin/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ var capabilities = []provider.Capability{
Name: "xml",
TemplateContext: openapi3.SchemaRef{},
},
{
Name: "xmlPublicID",
TemplateContext: openapi3.SchemaRef{},
},
{
Name: "json",
TemplateContext: openapi3.SchemaRef{},
Expand All @@ -54,6 +58,7 @@ type builtinCondition struct {
Filecontent fileContentCondition `yaml:"filecontent"`
File fileCondition `yaml:"file"`
XML xmlCondition `yaml:"xml"`
XMLPublicID xmlPublicIDCondition `yaml:"xmlPublicID"`
JSON jsonCondition `yaml:"json"`
HasTags []string `yaml:"hasTags"`
provider.ProviderContext `yaml:",inline"`
Expand All @@ -76,6 +81,12 @@ type xmlCondition struct {
Filepaths []string `yaml:"filepaths"`
}

type xmlPublicIDCondition struct {
Regex string `yaml:"regex"`
Namespaces map[string]string `yaml:"namespaces"`
Filepaths []string `yaml:"filepaths"`
}

type jsonCondition struct {
XPath string `yaml:'xpath'`
Filepaths []string `yaml:"filepaths"`
Expand Down
124 changes: 92 additions & 32 deletions provider/internal/builtin/service_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,46 +147,19 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
if query == nil || err != nil {
return response, fmt.Errorf("could not parse provided xpath query '%s': %v", cond.XML.XPath, err)
}
//TODO(fabianvf): how should we scope the files searched here?
var xmlFiles []string
patterns := []string{"*.xml", "*.xhtml"}
xmlFiles, err = provider.GetFiles(p.config.Location, cond.XML.Filepaths, patterns...)
xmlFiles, err := findXMLFiles(p.config.Location, cond.XMLPublicID.Filepaths)
if err != nil {
return response, fmt.Errorf("unable to find files using pattern `%s`: %v", patterns, err)
return response, fmt.Errorf("Unable to find XML files: %v", err)
}

for _, file := range xmlFiles {
f, err := os.Open(file)
nodes, err := queryXMLFile(file, query)
if err != nil {
fmt.Printf("unable to open file '%s': %v\n", file, err)
continue
}
// TODO This should start working if/when this merges and releases: https://github.com/golang/go/pull/56848
var doc *xmlquery.Node
doc, err = xmlquery.ParseWithOptions(f, xmlquery.ParserOptions{Decoder: &xmlquery.DecoderOptions{Strict: false}})
if err != nil {
if err.Error() == "xml: unsupported version \"1.1\"; only version 1.0 is supported" {
// TODO HACK just pretend 1.1 xml documents are 1.0 for now while we wait for golang to support 1.1
b, err := os.ReadFile(file)
if err != nil {
fmt.Printf("unable to parse xml file '%s': %v\n", file, err)
continue
}
docString := strings.Replace(string(b), "<?xml version=\"1.1\"", "<?xml version = \"1.0\"", 1)
doc, err = xmlquery.Parse(strings.NewReader(docString))
if err != nil {
fmt.Printf("unable to parse xml file '%s': %v\n", file, err)
continue
}
} else {
fmt.Printf("unable to parse xml file '%s': %v\n", file, err)
continue
}
}
list := xmlquery.QuerySelectorAll(doc, query)
if len(list) != 0 {
if len(nodes) != 0 {
response.Matched = true
for _, node := range list {
for _, node := range nodes {
ab, err := filepath.Abs(file)
if err != nil {
ab = file
Expand All @@ -210,6 +183,52 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
}
}

return response, nil
case "xmlPublicID":
regex, err := regexp.Compile(cond.XMLPublicID.Regex)
if err != nil {
return response, fmt.Errorf("Could not parse provided public-id regex '%s': %v", cond.XMLPublicID.Regex, err)
}
query, err := xpath.CompileWithNS("//*[@public-id]", cond.XMLPublicID.Namespaces)
if query == nil || err != nil {
return response, fmt.Errorf("Could not parse public-id xml query '%s': %v", cond.XML.XPath, err)
}
xmlFiles, err := findXMLFiles(p.config.Location, cond.XMLPublicID.Filepaths)
if err != nil {
return response, fmt.Errorf("Unable to find XML files: %v", err)
}

for _, file := range xmlFiles {
nodes, err := queryXMLFile(file, query)
if err != nil {
continue
}

for _, node := range nodes {
// public-id attribute regex match check
for _, attr := range node.Attr {
if attr.Name.Local == "public-id" {
if regex.MatchString(attr.Value) {
response.Matched = true
ab, err := filepath.Abs(file)
if err != nil {
ab = file
}
response.Incidents = append(response.Incidents, provider.IncidentContext{
FileURI: uri.File(ab),
Variables: map[string]interface{}{
"matchingXML": node.OutputXML(false),
"innerText": node.InnerText(),
"data": node.Data,
},
})
}
break
}
}
}
}

return response, nil
case "json":
query := cond.JSON.XPath
Expand Down Expand Up @@ -372,3 +391,44 @@ func findFilesMatchingPattern(root, pattern string) ([]string, error) {
})
return matches, err
}

func findXMLFiles(baseLocation string, filePaths []string) ([]string, error) {
patterns := []string{"*.xml", "*.xhtml"}
// TODO(fabianvf): how should we scope the files searched here?
xmlFiles, err := provider.GetFiles(baseLocation, filePaths, patterns...)
return xmlFiles, err
}

func queryXMLFile(filePath string, query *xpath.Expr) ([]*xmlquery.Node, error) {
f, err := os.Open(filePath)
if err != nil {
fmt.Printf("unable to open file '%s': %v\n", filePath, err)
return nil, err
}
defer f.Close()
// TODO This should start working if/when this merges and releases: https://github.com/golang/go/pull/56848
var doc *xmlquery.Node
doc, err = xmlquery.ParseWithOptions(f, xmlquery.ParserOptions{Decoder: &xmlquery.DecoderOptions{Strict: false}})
if err != nil {
if err.Error() == "xml: unsupported version \"1.1\"; only version 1.0 is supported" {
// TODO HACK just pretend 1.1 xml documents are 1.0 for now while we wait for golang to support 1.1
var b []byte
b, err = os.ReadFile(filePath)
if err != nil {
fmt.Printf("unable to parse xml file '%s': %v\n", filePath, err)
return nil, err
}
docString := strings.Replace(string(b), "<?xml version=\"1.1\"", "<?xml version = \"1.0\"", 1)
doc, err = xmlquery.Parse(strings.NewReader(docString))
if err != nil {
fmt.Printf("unable to parse xml file '%s': %v\n", filePath, err)
return nil, err
}
} else {
fmt.Printf("unable to parse xml file '%s': %v\n", filePath, err)
return nil, err
}
}
nodes := xmlquery.QuerySelectorAll(doc, query)
return nodes, err
}
5 changes: 5 additions & 0 deletions rule-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@
builtin.xml:
xpath:
invalid-query: "test"
- message: "JBoss 5.x EAR descriptor (jboss-app.xml) was found with public-id"
ruleID: jboss-eap5-7-xml-02000
when:
builtin.xmlPublicID:
regex: .*JBoss.+DTD Java EE.+5.*
- message: "Tags {{tags}} found, creating message and new tag both"
ruleID: multiple-actions-001
tag:
Expand Down

0 comments on commit ca8ced3

Please sign in to comment.