Repository: roaldnefs/salt-lint

Issue:
Suggested improvements/alternative to file mode implementation
The underlying issue here is that _numbers beginning with zero_ are interpreted as octal in YAML.

https://docs.saltstack.com/en/latest/ref/states/all/salt.states.file.html

> **Warning**
> 
> When using a mode that includes a leading zero you must wrap the value in single quotes. If the value is not wrapped in quotes it will be read by YAML as an integer and evaluated as an octal.

A useful look is how `yamllint` handles these.

https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.octal_values

* Ultimately, this is about any numbers beginning with `0`, not just file/dir modes.

I've done test runs on 45 formulas under the [SaltStack Formulas organisation](https://github.com/saltstack-formulas).  Many of them fall afoul of both `207` and `208`.  The problem is that most of these are _false positives_.  The very common usage of 3-digit, unquoted modes (e.g. `755` and `644`) is completely legitimate.  My suggestion is that these shouldn't be flagged as errors/warnings.  There's a simple workaround for this for the time being:

* Apply something similar to: https://github.com/myii/salt-lint/commit/177e3deffa17d47a4660e6096187f31fedbea908
* Add `208` to the ignored list

A more comprehensive solution could be achieved under a new lint ID, which checks for _any_ number with a leading zero (not just file/dir modes), as shown on the `yamllint` docs.  Then users could add both `207` and `208` to the ignore list and use the comprehensive _octal_ bug linter instead.

Comments:

Patch:
diff --git a/lib/saltlint/rules/YamlHasOctalValueRule.py b/lib/saltlint/rules/YamlHasOctalValueRule.py
new file mode 100644
index 0000000..2be6e5f
--- /dev/null
+++ b/lib/saltlint/rules/YamlHasOctalValueRule.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2016, Will Thames and contributors
+# Copyright (c) 2018, Ansible Project
+# Modified work Copyright (c) 2019 Roald Nefs
+
+from saltlint import SaltLintRule
+import re
+
+
+class YamlHasOctalValueRule(SaltLintRule):
+    id = '210'
+    shortdesc = 'Numbers that start with `0` should always be encapsulated in quotation marks'
+    description = 'Numbers that start with `0` should always be encapsulated in quotation marks'
+    severity = 'HIGH'
+    tags = ['formatting']
+    version_added = 'develop'
+
+    bracket_regex = re.compile(r"(?<!['\"])0+[1-9]\d*(?!['\"])")
+
+    def match(self, file, line):
+        return self.bracket_regex.search(line)


Test patch:
diff --git a/tests/unit/TestYamlHasOctalValueRule.py b/tests/unit/TestYamlHasOctalValueRule.py
new file mode 100644
index 0000000..996fa63
--- /dev/null
+++ b/tests/unit/TestYamlHasOctalValueRule.py
@@ -0,0 +1,48 @@
+# Copyright (c) 2013-2018 Will Thames <will@thames.id.au>
+# Copyright (c) 2018 Ansible by Red Hat
+# Modified work Copyright (c) 2019 Roald Nefs
+
+import unittest
+
+from saltlint import RulesCollection
+from saltlint.rules.YamlHasOctalValueRule import YamlHasOctalValueRule
+from tests import RunFromText
+
+
+GOOD_NUMBER_LINE = '''
+testdirectory:
+  file.recurse:
+    - name: /tmp/directory
+    - file_mode: 700
+    - dir_mode: '0775'
+
+testdirectory2:
+  file.recurse:
+    - name: /tmp/directory2
+    - file_mode: 0
+    - dir_mode: "0775"
+'''
+
+BAD_NUMBER_LINE = '''
+testdirectory:
+  file.recurse:
+    - name: /tmp/directory
+    - file_mode: 0775
+    - dir_mode: 070
+'''
+
+class TestFileModeLeadingZeroRule(unittest.TestCase):
+    collection = RulesCollection()
+
+    def setUp(self):
+        self.collection.register(YamlHasOctalValueRule())
+
+    def test_statement_positive(self):
+        runner = RunFromText(self.collection)
+        results = runner.run_state(GOOD_NUMBER_LINE)
+        self.assertEqual(0, len(results))
+
+    def test_statement_negative(self):
+        runner = RunFromText(self.collection)
+        results = runner.run_state(BAD_NUMBER_LINE)
+        self.assertEqual(2, len(results))