summaryrefslogtreecommitdiff
path: root/lib/chef/resource/windows_defender_exclusion.rb
blob: ad9afd77e22ad59b81626c50307e1d490161ecf0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#
# Copyright:: Chef Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require_relative "../resource"

class Chef
  class Resource
    class WindowsDefenderExclusion < Chef::Resource

      provides :windows_defender_exclusion

      description "Use the **windows_defender_exclusion** resource to exclude paths, processes, or file types from Windows Defender realtime protection scanning."
      introduced "17.3"
      examples <<~DOC
      **Add excluded items to Windows Defender scans**:

      ```ruby
      windows_defender_exclusion 'Add to things to be excluded from scanning' do
        paths 'c:\\foo\\bar, d:\\bar\\baz'
        extensions 'png, foo, ppt, doc'
        process_paths 'c:\\windows\\system32'
        action :add
      end
      ```

      **Remove excluded items from Windows Defender scans**:

      ```ruby
      windows_defender_exclusion 'Remove things from the list to be excluded' do
        process_paths 'c:\\windows\\system32'
        action :remove
      end
      ```
      DOC
      unified_mode true

      property :paths, [String, Array], default: [],
        coerce: proc { |x| to_consistent_path_array(x) },
        description: "File or directory paths to exclude from scanning."

      property :extensions, [String, Array], default: [],
        coerce: proc { |x| to_consistent_path_array(x) },
        description: "File extensions to exclude from scanning."

      property :process_paths, [String, Array], default: [],
        coerce: proc { |x| to_consistent_path_array(x) },
        description: "Paths to executables to exclude from scanning."

      def to_consistent_path_array(x)
        fixed = x.dup || []
        fixed = fixed.split(/\s*,\s*/) if fixed.is_a?(String)
        fixed.map!(&:downcase)
        fixed.map! { |v| v.gsub(%r{/}, "\\") }
        fixed
      end

      load_current_value do |new_resource|
        Chef::Log.debug("Running 'Get-MpPreference | Select-Object ExclusionExtension,ExclusionPath,ExclusionProcess' to get Windows Defender State")

        values = powershell_exec!("Get-MPpreference | Select-Object ExclusionExtension,ExclusionPath,ExclusionProcess").result

        values.transform_values! { |x| Array(x) }

        paths new_resource.paths & values["ExclusionPath"]
        extensions new_resource.extensions & values["ExclusionExtension"]
        process_paths new_resource.process_paths & values["ExclusionProcess"]
      end

      action :add do
        converge_if_changed do
          powershell_exec!(add_cmd)
        end
      end

      action :remove do
        converge_if_changed do
          powershell_exec!(remove_cmd)
        end
      end

      action_class do
        MAPPING = {
          paths: "ExclusionPath",
          extensions: "ExclusionExtension",
          process_paths: "ExclusionProcess",
        }.freeze

        def add_cmd
          cmd = "Add-MpPreference -Force"

          MAPPING.each do |prop, flag|
            to_add = new_resource.send(prop) - current_resource.send(prop)
            cmd << " -#{flag} #{to_add.join(",")}" unless to_add.empty?
          end

          cmd
        end

        def remove_cmd
          cmd = "Remove-MpPreference -Force"

          MAPPING.each do |prop, flag|
            to_add = new_resource.send(prop) & current_resource.send(prop)
            cmd << " -#{flag} #{to_add.join(",")}" unless to_add.empty?
          end

          cmd
        end
      end
    end
  end
end