DevSkim icon indicating copy to clipboard operation
DevSkim copied to clipboard

Is it possible to match something that is missing in a file?

Open JaneX8 opened this issue 1 year ago • 5 comments

Is it possible to match something that is missing in a file? For example if I apply a DevSkim rule on all yaml files or better if possible on all files ending with .sometool.yaml, can I then specifically trigger a warning on something that does not exist? For example the file is expected to have a certain option but it doesn't contain it.

I tried to achieve it with regex negative lookaheads but it doesn't seem possible, I get the self-tests (must-match / must-not-match) to work but not triggering on the actual content.

JaneX8 avatar Dec 06 '24 11:12 JaneX8

I don't think this is something I've tried before, but can you share the sample that worked with match/must-not match? If it passes those tests I think it should be possible to make it work, but it may be that there's a check somewhere in the conversion from match to issue object creation/output creation that is swallowing it. If you share the sample rule I could debug in and see where it might be getting stuck.

gfs avatar Dec 06 '24 18:12 gfs

A very minimal test for negative lookaheads:

xyz.devskim.json:

[{
    "name": "Ensure 'xyz' is used",
    "id": "ensure_xyz_is_used",
    "description": "The string 'xyz' must be present.",
    "recommendation": "Ensure that the string 'xyz' is included anywhere.",
    "tags": [
        "test"
    ],
    "confidence": "high",
    "severity": "important",
    "patterns": [{
        "pattern": "^(?!.*xyz).*$",
        "type": "regex",
        "scopes": [
            "code"
        ]
    }],
    "must-match": [
        "abc",
        "dee ghi"
    ],
    "must-not-match": [
        "abc xyz",
        "xyz abc"
    ]
}]
devskim verify -r ".\xyz.devskim.json"                                                                         
[21:10:40 INF] 1 of 1 rules have must-match self-tests.
[21:10:40 INF] 1 of 1 rules have must-not-match self-tests.

If I use this rule one a simple text file with one line that does not contain or does contain, it works as expected as well.

The negative lookahead seems to work in self-test and on one-line files, although I'm having trouble getting it to work when I include newlines and tabs in the must-match and must-not-match tests (such as with yaml). Even with modifier m, which I guess is multiline? I'm trying to use this on a yaml file. I want to detect is a certain key say xyz: is used. Preferably only with, or in combination with ymlpath and regex, so that I can also check the specific location in the yaml structure. But I have no idea how to pull that of yet.

JaneX8 avatar Dec 06 '24 20:12 JaneX8

I just remembered this is possible using conditions. Here I just set the initial pattern to something that matches everything, and then leverage the negate_finding feature of the condition mechanism.

[{
    "name": "Ensure 'xyz' is used",
    "id": "ensure_xyz_is_used",
    "description": "The string 'xyz' must be present.",
    "recommendation": "Ensure that the string 'xyz' is included anywhere.",
    "tags": [
        "test"
    ],
    "confidence": "high",
    "severity": "important",
    "patterns": [{
        "pattern": ".*",
        "type": "regex",
        "scopes": [
            "code"
        ]
    }],
   "conditions": [
      {
        "pattern": {
          "pattern": "xyz",
          "type": "string",
          "scopes": [
            "code"
          ],
          "modifiers": [
            "i"
          ]
        },
        "search_in": "same-file",
        "negate_finding": true
      }
    ],
    "must-match": [
        "abc",
        "dee ghi"
    ],
    "must-not-match": [
        "abc xyz",
        "xyz abc"
    ]
}]

I believe you can add newlines to the must-match and must-not-match samples using \\n.

gfs avatar Dec 06 '24 20:12 gfs

Thank you. That's great and it works with \n\t in the the self-tests. But the outcome of .* is that every char and line is red underlined now. Logically so, there is nothing to match (or negative everything to match). I'm not sure how to practically and properly implement this behavior with non-matchers in a usable way.

Yes I can use ^(.) and just match everything on the first char on the first line, but this doesn't structurally solve it. Also it makes fixits unusable. If I could somehow combine this with ymlpath (same for xpath/jsonpath) matchers, we could even construct a fixit that suggests where to insert the missing line or word approximately, I think.

JaneX8 avatar Dec 06 '24 21:12 JaneX8

You should be able to substitute any pattern you want (including ymlpath etc) in the initial pattern for what you would want highlighted - I just left it very broad for the example. That should then only be identified as an issue in the absence of the condition negating the finding (which could additionally be a different ymlpath query if desired), but functionally you'd need something to hook onto for the original match before being potentially negated.

gfs avatar Dec 06 '24 21:12 gfs