Bug #12479

Regular expression matching against an undefined variable results in matching the string `undef`

Added by Nicolas Simonds 3 months ago. Updated about 1 month ago.

Status:Closed Start date:02/06/2012
Priority:Normal Due date:
Assignee:- % Done:

0%

Category:parser
Target version:2.7.14
Affected Puppet version:2.6.0 Branch:https://github.com/puppetlabs/puppet/pull/470
Keywords:
Votes: 0

Description

Non-existent variables are stringify-ing under the hood as "undef", which causes them to match regexes, despite behaving like nil or the empty string when otherwise asked.

A simple demonstration manifest:

case $wtf { "undef": { notice("matched string undef") } }
case $wtf { /^undef$/: { notice("matched regexp undef") } }
case $wtf { "": { notice("matched empty string") } }
case $wtf { undef: { notice("matched bareword undef") } }

Expected result:

All notices that the non-existent fact or variable doesn’t match.

Actual result:

Various and sundry notices that it matched. BAD PUPPET! NO COOKIE!

All die of embarrassment.

History

Updated by Nicolas Simonds 3 months ago

What in the world did the bugtracker do to my script?! Let’s try that again:

#!/bin/bash
for LOLWUT in {a..z} do

    cat <<- EOF > /tmp/test.pp
    case \$wtf {
            /$LOLWUT/: {
                    notice("The fact[\$wtf] matches the regex /$LOLWUT/")
            }
            default: {
                    notice("The fact[\$wtf] does NOT match the regex /$LOLWUT/")
            }
    }
    notice(\$facility)
    EOF

    puppet apply /tmp/test.pp
done

Updated by Nicolas Simonds 3 months ago

Where this gets us in trouble is cases like:

case $schrodingers_fact {
    /\w/: {
        notice("fact is set.  hooray!")
    }
    default: {
        notice("fact is not set.  set it")
        $schrodingers_fact = retrieve_fact_via_carrier_pigeon()
    }
}

…because now Puppet seems to think the fact doesn’t need to be set, when it does. This idiom worked swimmingly in Puppet 0.25.x, and seems to have stopped working in the 2.6.x vintage.

Updated by Nicolas Simonds 3 months ago

A look over the results of the test script shows it matching on the letters D, E, F, N, and U. I’m no expert, but I’m pretty sure those letters spell “undef”.

Updated by Jonathan Boyett 3 months ago

↑ This is funny.

Updated by Daniel Pittman 3 months ago

  • Subject changed from Regression: Regex matching against non-existant facts results in false-positive to Regular expression matching against an undefined variable results in matching the string `undef`
  • Description updated (diff)
  • Category set to parser
  • Status changed from Unreviewed to In Topic Branch Pending Review
  • Target version set to 2.7.x
  • Affected Puppet version set to 2.6.0
  • Branch set to https://github.com/puppetlabs/puppet/pull/470

Jonathan Boyett wrote:

↑ This is funny.

I think you misspelled “humiliating” there. ;)

So, it turns out this comes from some other deliberate behaviour: https://github.com/puppetlabs/puppet/commit/53869e99149be0f60b4e415d061a76ab5421eadb

A sorry side effect of that is that the regexp match turns it into a string, and matches on that, even though it shouldn’t.

Matching undef against a literal string defines undef == "", so for consistency in the 2.7 branch I am going to define undef =~ /^$/ – it matches the literal empty string also.

Arguments that this undef == empty string behaviour is insane happily accepted on another ticket. (eg: #6745, but there are doubtless others :)

https://github.com/puppetlabs/puppet/pull/470 contains the fix, if anyone wants to go ahead and test this before we merge it, but it restore the old (and saner) behaviour. Now your 0.25 idiom will actually work. (Also, we have some tests, so it won’t break again accidentally.)

Updated by Nicolas Simonds 3 months ago

A workaround from the manifest side, for those of us who get our software from upstream packages and don’t modify it:

case $schrodingers_fact {
    /^undef$/: { notice("do the non-matching stuff here") }
    /\w/:      { notice("do the matching stuff here") }
    default:   { notice("do the non-matching stuff here") }
}

Yes, it’s icky, and it involves repeating repeating yourself. That’s why they call them bugs.

Updated by Daniel Pittman 3 months ago

You can also use the empty (literal) string for the non-matching case, if you want; that will actually be safe in the longest term, assuming that “I have an empty string” is also a failure case for your fact. :)

Updated by Daniel Pittman about 1 month ago

  • Status changed from In Topic Branch Pending Review to Merged - Pending Release
  • Target version changed from 2.7.x to 2.7.13

Updated by Matthaus Litteken about 1 month ago

  • Target version changed from 2.7.13 to 2.7.14

Updated by Matthaus Litteken about 1 month ago

  • Status changed from Merged - Pending Release to Closed

Released in Puppet 2.7.14rc1

Also available in: Atom PDF