Godzilla74
Godzilla74

Reputation: 2502

Nokogiri condition based on attribute value

I'm attempting to potentially parse two XML files with Nokogiri, which is based on an attribute value existing. The problem is that my initial conditional is always returning true, even if the node doesn't exist at the xpath. I think I've done everything right, but obviously I haven't.

Here are my two XML files:

dnsrecon_output.xml (typical file output)

<?xml version="1.0" ?>
<records>
    <record ns_server="192.168.1.1" type="info" zone_transfer="failed"/>
    <record ns_server="192.168.1.2" type="info" zone_transfer="failed"/>
    <record address="192.168.1.1" mname="ns1.mynetwork.local" type="SOA"/>
    <record Version="9.8.2rc1-RedHat-9.8.2-0.37.rc1.el6_7.5" address="192.168.1.1" recursive="True" target="ns1.mynetwork.local" type="NS"/>
    <record Version="9.8.2rc1-RedHat-9.8.2-0.37.rc1.el6_7.5" address="192.168.1.2" recursive="True" target="ns2.mynetwork.local" type="NS"/>
    <record address="192.168.1.3" name="web.local" type="A"/>
</records>

dnsrecon_brute.xml

<?xml version="1.0" ?>
<records>
    <record address="192.168.1.3" name="dev.web.local" type="A"/>
    <record name="ftp.web.local" target="dev.web.local" type="CNAME"/>
    <record address="192.168.1.4" name="git.web.local" type="A"/>
    <record address="192.168.1.5" name="ops.web.local" type="A"/>
    <record address="192.168.1.6" name="whm.web.local" type="A"/>
    <record address="192.168.1.7" name="www.web.local" type="A"/>
</records>

However, if zone_transfer=success the output will look like this...

dnsrecon_output_xfer.xml (just to show what I'm trying to match)

<?xml version="1.0" ?>
<records>
    <record ns_server="192.168.1.2" type="info" zone_transfer="success"/>
    <record address="192.168.1.2" mname="ns" type="SOA" zone_server="192.168.1.2"/>
    <record address="192.168.1.2" target="ns1.network.local" type="NS" zone_server="192.168.1.2"/>
    <record address="192.168.1.100" name="win.network.local" type="A" zone_server="192.168.1.2"/>
    <record address="192.168.1.2" name="ns1.network.local" type="A" zone_server="192.168.1.2"/>
    <record address="192.168.1.2" mname="ns1.network.local" type="SOA"/>
</records>

And here's my Nokogiri parsing. Mainly I want to check to see if the zone_transfer=success exists, if it doesn't move on to the *_brute.xml file:

document = Nokogiri::XML(File.open("dnsrecon_output.xml"))

document.remove_namespaces!

# Strictly for Zone transfers
if document.xpath("//records//record[@zone_transfer='success']")
  puts "Zone Transfer Successful ... YIKES!!!"
  document.xpath("//records//record[@type='A']").each do |record|
    # change the output here to match what we need it to...
    puts "#{record.at_xpath("@address")} - #{record.at_xpath("@name")}"
  end
else
  puts "No zone transfers... moving to brute"
  document2 = Nokogiri::XML(File.open("dnsrecon_brute.xml"))
  document2.remove_namespaces!

  document2.xpath("//records//record[@type='A' or @type='CNAME']").each do |record|
    puts "#{record.at_xpath("@address")} - #{record.at_xpath("@name")}"
  end
end

As you'll note, I'm initially parsing the dnsrecon_output.xml file, which has the zone_transfer="failed" in the record tag. However, my output in my console is:

Zone Transfer Successful ... YIKES!!!
192.168.1.3 - web.local

And never runs the else portion, which is should. Where have I gone wrong with the attribute value checking?

Upvotes: 0

Views: 3068

Answers (2)

Pascal
Pascal

Reputation: 8646

Your check returns an empty Nokogiri::XML::NodeSet which is truthy.

Either read the attribute and compare its value:

if document.xpath("//records//record/@zone_transfer").first.value == 'success'
    ...

Note that this will only work if the xpath matches at least one element (so it wont work for the "brute" file).

or add a any? to get a boolean return:

if document.xpath("//records//record[@zone_transfer='success']").any?
    ...

Upvotes: 3

7stud
7stud

Reputation: 48599

x = document.xpath("//records//record[@zone_transfer='success']")
p x

--output:--
[]

and:

if []
  puts %q{It's true!}
end

--output:--
It's true.

The only values that evaluate to false in ruby are false itself and nil. Empty arrays, empty hashes, blank strings, 0, etc. are all true.

Instead, you can do something like this:

if document.xpath("//records//record[@zone_transfer='success']").empty?
  puts "no matching elements"
  #got to next xml file
else
  puts "found matches"
  #do your other xpath search
end

--output:--
no matching elements

Upvotes: 0

Related Questions