Overzealous regexp validation or my mistake?

16 views
Skip to first unread message

Danny Sauer

unread,
Sep 19, 2014, 2:36:07 PM9/19/14
to help-c...@googlegroups.com


So, I have a policy that reads in a config file which sets permissions on some files.  The structure of the file is like

/path/to/file:mode:user:group

In some instances, the business area only cares what the user is, and the group should be left alone.  In those instances, the line ends with a colon.  That means that sometimes I don't have a group specified.  The solution I have is to read the file twice using readstringarray, marking the lines which end with a colon as comments in one, and treating lines which don't end with a colon as comments in the other.  I also need to treat #.* as a comment (but only on a line by itself) in both cases.  So, the chunk of policy looks basically like this:

"names_w_grp_count"
 
int => readstringarray(
   
"names_with_group",
   
"$(perm_file)",
   
"(\s*#[^\n]*)|(^.[^\n]*:\s*(\n|\z))",
   
":",
   
"inf",
   
"inf"
   
),
  comment
=> "Comment expression prunes lines which end with a colon";
"names_w_no_grp_count"
 
int => readstringarray(
   
"names_with_no_group",
   
"$(perm_file)",
   
"(\s*#[^\n]*)|(^.(?![^\n]*:\s*(\n|\z))[^\n]*(\n|\z))",
   
":",
   
"inf",
   
"inf"
   
),
  comment
=> "Comment expression prunes lines which do not end with a colon";

I've tested both expressions using pcregrep, and each line appropriately #comments as well as the appropriate lines which either end or don't-end with colons (possibly with trailing whitespace).  However, CFEngine doesn't seem to like the second expression (the one with the zero-width negative look-ahead).  It gives this error message:

error: Comment regex '(\s*#[^\n]*)|(^.(?![^\n]*:\s*(\n|\z))[^\n]*(\n|\z))' was irreconcilable reading input '/var/cfengine/templates/thefilename' probably because it legally matches nothing

The expression matches several things just fine, as demonstrated here:

user@host [/home/user]
$ cat testfile
#comment
file:user:
file:user:group
user@host [/home/user]
$ pcregrep --color "(\s*#[^\n]*)|(^.[^\n]*:\s*(\n|\z))" testfile
#comment
file:user:

user@host [/home/user]
$ pcregrep --color "(\s*#[^\n]*)|(^.(?![^\n]*:\s*(\n|\z))[^\n]*(\n|\z))" testfile
#comment
file:user:group


...So basically I'm looking for suggestions as to how I can trick CFEngine (3.5.x) into accepting this as valid. :)

Neil Watson

unread,
Sep 19, 2014, 2:55:55 PM9/19/14
to help-c...@googlegroups.com
Hi Danny,

Sometimes CFEngine can be snooty and think it knows better than us.
This is an example and so is the hard limit for string sizes.

You might be able to work around this by determining if the group has
been provided by checking the contents of the names_with_group array. If
the group is not there set a class "promise_perms_without_group" and if
it's there "promiser_perms_with_group".

Sincerely,
--
Neil H Watson
Sr. Partner, Architecture and Infrastructure
CFEngine reporting: https://github.com/evolvethinking/delta_reporting
CFEngine policy: https://github.com/evolvethinking/evolve_cfengine_freelib
CFEngine and vim: https://github.com/neilhwatson/vim_cf3
CFEngine support: http://evolvethinking.com

Neil Watson

unread,
Sep 19, 2014, 3:12:48 PM9/19/14
to help-c...@googlegroups.com
Danny,

Try this:

body common control
{
bundlesequence => {
"main",
};
}

bundle agent main
{
methods:

"any" usebundle => test;
}

bundle agent test
{
vars:
"f[0][0]" string => "/path/to/file";
"f[0][1]" string => "644";
"f[0][2]" string => "neil";
"f[0][3]" string => "cfengineers";

"f[1][0]" string => "/another/file";
"f[1][1]" string => "755";
"f[1][2]" string => "root";
# Notice no group for date line 1;

"i" slist => getindices( "f" );

# Use references for simpler reading
"file[${i}]" string => "f[${i}][0]";
"mode[${i}]" string => "f[${i}][1]";
"use[${i}]" string => "f[${i}][2]";
"group[${i}]" string => "f[${i}][3]";

classes:
"promise_perms_with_group_${i}"
expression => isvariable( "f[${i}][3]" );
"promise_perms_without_group_${i}"
not => isvariable( "f[${i}][3]" );

reports:
"Promising group for ${${file[${i}]}} [${${group[${i}]}}]"
ifvarclass => "promise_perms_with_group_${i}";
"Promising without group for ${${file[${i}]}} [${${group[${i}]}}]"
ifvarclass => "promise_perms_without_group_${i}";
}

Produces:
R: Promising group for /path/to/file [cfengineers]
R: Promising without group for /another/file [${f[1][3]}]

Danny Sauer

unread,
Sep 22, 2014, 3:51:47 PM9/22/14
to help-c...@googlegroups.com, cfen...@watson-wilson.ca
On Friday, September 19, 2014 2:12:48 PM UTC-5, Neil Watson wrote:
Danny,

Try this:

Thanks, Neil. I appreciate the suggestion.  That was an early thought, but I've adopted a policy of avoiding using the "test to see if something doesn't exist" approach when that "something" is something that CFEngine will define.  The seemingly non-deterministic way that variables and classes are expanded has burned me a number of times, leading to "a_is_not_defined" and "a_is_defined" classes existing simultaneously in my policy and making things quite challenging to debug.  In the interest of expediency, we ended up just moving the definitions into two separate files and basically duplicating all of the code for each file.  At some point, I still intend to dig in to the agent code to see where in the heck this regex checking is going wrong.  I suspect it's because of "you have stuff other than a paren after a \z," but without actually looking, I can't know for sure. :)
Reply all
Reply to author
Forward
0 new messages