inspec 2.0.32 → 2.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +101 -101
- data/CHANGELOG.md +2991 -2970
- data/Gemfile +55 -55
- data/LICENSE +14 -14
- data/MAINTAINERS.md +33 -33
- data/MAINTAINERS.toml +52 -52
- data/README.md +446 -437
- data/Rakefile +322 -322
- data/bin/inspec +12 -12
- data/docs/.gitignore +2 -2
- data/docs/README.md +40 -40
- data/docs/dsl_inspec.md +258 -258
- data/docs/dsl_resource.md +93 -93
- data/docs/glossary.md +99 -99
- data/docs/habitat.md +191 -191
- data/docs/inspec_and_friends.md +107 -107
- data/docs/matchers.md +169 -168
- data/docs/migration.md +293 -293
- data/docs/platforms.md +118 -118
- data/docs/plugin_kitchen_inspec.md +49 -49
- data/docs/profiles.md +370 -370
- data/docs/reporters.md +105 -105
- data/docs/resources/aide_conf.md.erb +75 -75
- data/docs/resources/apache.md.erb +67 -67
- data/docs/resources/apache_conf.md.erb +68 -68
- data/docs/resources/apt.md.erb +71 -71
- data/docs/resources/audit_policy.md.erb +47 -47
- data/docs/resources/auditd.md.erb +79 -79
- data/docs/resources/auditd_conf.md.erb +68 -68
- data/docs/resources/aws_cloudtrail_trail.md.erb +140 -140
- data/docs/resources/aws_cloudtrail_trails.md.erb +81 -81
- data/docs/resources/aws_cloudwatch_alarm.md.erb +86 -86
- data/docs/resources/aws_cloudwatch_log_metric_filter.md.erb +151 -151
- data/docs/resources/aws_config_recorder.md.erb +71 -71
- data/docs/resources/aws_ec2_instance.md.erb +106 -106
- data/docs/resources/aws_iam_access_key.md.erb +123 -123
- data/docs/resources/aws_iam_access_keys.md.erb +198 -198
- data/docs/resources/aws_iam_group.md.erb +46 -46
- data/docs/resources/aws_iam_groups.md.erb +43 -43
- data/docs/resources/aws_iam_password_policy.md.erb +76 -76
- data/docs/resources/aws_iam_policies.md.erb +82 -82
- data/docs/resources/aws_iam_policy.md.erb +144 -144
- data/docs/resources/aws_iam_role.md.erb +63 -63
- data/docs/resources/aws_iam_root_user.md.erb +58 -58
- data/docs/resources/aws_iam_user.md.erb +64 -64
- data/docs/resources/aws_iam_users.md.erb +89 -89
- data/docs/resources/aws_kms_keys.md.erb +84 -84
- data/docs/resources/aws_route_table.md.erb +47 -47
- data/docs/resources/aws_s3_bucket.md.erb +134 -134
- data/docs/resources/aws_security_group.md.erb +151 -151
- data/docs/resources/aws_security_groups.md.erb +91 -91
- data/docs/resources/aws_sns_topic.md.erb +63 -63
- data/docs/resources/aws_subnet.md.erb +133 -133
- data/docs/resources/aws_subnets.md.erb +126 -126
- data/docs/resources/aws_vpc.md.erb +120 -120
- data/docs/resources/aws_vpcs.md.erb +48 -48
- data/docs/resources/azure_generic_resource.md.erb +170 -170
- data/docs/resources/azure_resource_group.md.erb +284 -284
- data/docs/resources/azure_virtual_machine.md.erb +347 -347
- data/docs/resources/azure_virtual_machine_data_disk.md.erb +224 -224
- data/docs/resources/bash.md.erb +75 -75
- data/docs/resources/bond.md.erb +90 -90
- data/docs/resources/bridge.md.erb +57 -57
- data/docs/resources/bsd_service.md.erb +67 -67
- data/docs/resources/command.md.erb +138 -138
- data/docs/resources/cpan.md.erb +79 -79
- data/docs/resources/cran.md.erb +64 -64
- data/docs/resources/crontab.md.erb +89 -89
- data/docs/resources/csv.md.erb +54 -54
- data/docs/resources/dh_params.md.erb +205 -205
- data/docs/resources/directory.md.erb +30 -30
- data/docs/resources/docker.md.erb +219 -219
- data/docs/resources/docker_container.md.erb +104 -104
- data/docs/resources/docker_image.md.erb +94 -94
- data/docs/resources/docker_service.md.erb +114 -114
- data/docs/resources/elasticsearch.md.erb +242 -242
- data/docs/resources/etc_fstab.md.erb +125 -125
- data/docs/resources/etc_group.md.erb +75 -75
- data/docs/resources/etc_hosts.md.erb +78 -78
- data/docs/resources/etc_hosts_allow.md.erb +74 -74
- data/docs/resources/etc_hosts_deny.md.erb +74 -74
- data/docs/resources/file.md.erb +526 -515
- data/docs/resources/filesystem.md.erb +41 -41
- data/docs/resources/firewalld.md.erb +107 -107
- data/docs/resources/gem.md.erb +79 -79
- data/docs/resources/group.md.erb +61 -61
- data/docs/resources/grub_conf.md.erb +101 -101
- data/docs/resources/host.md.erb +86 -86
- data/docs/resources/http.md.erb +196 -196
- data/docs/resources/iis_app.md.erb +122 -122
- data/docs/resources/iis_site.md.erb +135 -135
- data/docs/resources/inetd_conf.md.erb +94 -94
- data/docs/resources/ini.md.erb +76 -76
- data/docs/resources/interface.md.erb +58 -58
- data/docs/resources/iptables.md.erb +64 -64
- data/docs/resources/json.md.erb +63 -63
- data/docs/resources/kernel_module.md.erb +120 -120
- data/docs/resources/kernel_parameter.md.erb +53 -53
- data/docs/resources/key_rsa.md.erb +85 -85
- data/docs/resources/launchd_service.md.erb +57 -57
- data/docs/resources/limits_conf.md.erb +75 -75
- data/docs/resources/login_def.md.erb +71 -71
- data/docs/resources/mount.md.erb +69 -69
- data/docs/resources/mssql_session.md.erb +60 -60
- data/docs/resources/mysql_conf.md.erb +99 -99
- data/docs/resources/mysql_session.md.erb +74 -74
- data/docs/resources/nginx.md.erb +79 -79
- data/docs/resources/nginx_conf.md.erb +128 -128
- data/docs/resources/npm.md.erb +60 -60
- data/docs/resources/ntp_conf.md.erb +60 -60
- data/docs/resources/oneget.md.erb +53 -53
- data/docs/resources/oracledb_session.md.erb +52 -52
- data/docs/resources/os.md.erb +141 -141
- data/docs/resources/os_env.md.erb +78 -78
- data/docs/resources/package.md.erb +120 -120
- data/docs/resources/packages.md.erb +67 -67
- data/docs/resources/parse_config.md.erb +103 -103
- data/docs/resources/parse_config_file.md.erb +138 -138
- data/docs/resources/passwd.md.erb +141 -141
- data/docs/resources/pip.md.erb +67 -67
- data/docs/resources/port.md.erb +137 -137
- data/docs/resources/postgres_conf.md.erb +79 -79
- data/docs/resources/postgres_hba_conf.md.erb +93 -93
- data/docs/resources/postgres_ident_conf.md.erb +76 -76
- data/docs/resources/postgres_session.md.erb +69 -69
- data/docs/resources/powershell.md.erb +102 -102
- data/docs/resources/processes.md.erb +109 -109
- data/docs/resources/rabbitmq_config.md.erb +41 -41
- data/docs/resources/registry_key.md.erb +158 -158
- data/docs/resources/runit_service.md.erb +57 -57
- data/docs/resources/security_policy.md.erb +47 -47
- data/docs/resources/service.md.erb +121 -121
- data/docs/resources/shadow.md.erb +146 -144
- data/docs/resources/ssh_config.md.erb +80 -80
- data/docs/resources/sshd_config.md.erb +83 -83
- data/docs/resources/ssl.md.erb +119 -119
- data/docs/resources/sys_info.md.erb +42 -42
- data/docs/resources/systemd_service.md.erb +57 -57
- data/docs/resources/sysv_service.md.erb +57 -57
- data/docs/resources/upstart_service.md.erb +57 -57
- data/docs/resources/user.md.erb +140 -140
- data/docs/resources/users.md.erb +127 -127
- data/docs/resources/vbscript.md.erb +55 -55
- data/docs/resources/virtualization.md.erb +57 -57
- data/docs/resources/windows_feature.md.erb +47 -47
- data/docs/resources/windows_hotfix.md.erb +53 -53
- data/docs/resources/windows_task.md.erb +95 -95
- data/docs/resources/wmi.md.erb +81 -81
- data/docs/resources/x509_certificate.md.erb +151 -151
- data/docs/resources/xinetd_conf.md.erb +156 -156
- data/docs/resources/xml.md.erb +85 -85
- data/docs/resources/yaml.md.erb +69 -69
- data/docs/resources/yum.md.erb +98 -98
- data/docs/resources/zfs_dataset.md.erb +53 -53
- data/docs/resources/zfs_pool.md.erb +47 -47
- data/docs/ruby_usage.md +203 -203
- data/docs/shared/matcher_be.md.erb +1 -1
- data/docs/shared/matcher_cmp.md.erb +43 -43
- data/docs/shared/matcher_eq.md.erb +3 -3
- data/docs/shared/matcher_include.md.erb +1 -1
- data/docs/shared/matcher_match.md.erb +1 -1
- data/docs/shell.md +215 -215
- data/examples/README.md +8 -8
- data/examples/inheritance/README.md +65 -65
- data/examples/inheritance/controls/example.rb +14 -14
- data/examples/inheritance/inspec.yml +15 -15
- data/examples/kitchen-ansible/.kitchen.yml +25 -25
- data/examples/kitchen-ansible/Gemfile +19 -19
- data/examples/kitchen-ansible/README.md +53 -53
- data/examples/kitchen-ansible/files/nginx.repo +6 -6
- data/examples/kitchen-ansible/tasks/main.yml +16 -16
- data/examples/kitchen-ansible/test/integration/default/default.yml +5 -5
- data/examples/kitchen-ansible/test/integration/default/web_spec.rb +28 -28
- data/examples/kitchen-chef/.kitchen.yml +20 -20
- data/examples/kitchen-chef/Berksfile +3 -3
- data/examples/kitchen-chef/Gemfile +19 -19
- data/examples/kitchen-chef/README.md +27 -27
- data/examples/kitchen-chef/metadata.rb +7 -7
- data/examples/kitchen-chef/recipes/default.rb +6 -6
- data/examples/kitchen-chef/recipes/nginx.rb +30 -30
- data/examples/kitchen-chef/test/integration/default/web_spec.rb +28 -28
- data/examples/kitchen-puppet/.kitchen.yml +22 -22
- data/examples/kitchen-puppet/Gemfile +20 -20
- data/examples/kitchen-puppet/Puppetfile +25 -25
- data/examples/kitchen-puppet/README.md +53 -53
- data/examples/kitchen-puppet/manifests/site.pp +33 -33
- data/examples/kitchen-puppet/metadata.json +11 -11
- data/examples/kitchen-puppet/test/integration/default/web_spec.rb +28 -28
- data/examples/meta-profile/README.md +37 -37
- data/examples/meta-profile/controls/example.rb +13 -13
- data/examples/meta-profile/inspec.yml +13 -13
- data/examples/profile-attribute.yml +2 -2
- data/examples/profile-attribute/README.md +14 -14
- data/examples/profile-attribute/controls/example.rb +11 -11
- data/examples/profile-attribute/inspec.yml +8 -8
- data/examples/profile-aws/controls/iam_password_policy_expiration.rb +8 -8
- data/examples/profile-aws/controls/iam_password_policy_max_age.rb +8 -8
- data/examples/profile-aws/controls/iam_root_user_mfa.rb +8 -8
- data/examples/profile-aws/controls/iam_users_access_key_age.rb +8 -8
- data/examples/profile-aws/controls/iam_users_console_users_mfa.rb +8 -8
- data/examples/profile-aws/inspec.yml +11 -11
- data/examples/profile-azure/controls/azure_resource_group_example.rb +24 -24
- data/examples/profile-azure/controls/azure_vm_example.rb +29 -29
- data/examples/profile-azure/inspec.yml +11 -11
- data/examples/profile-sensitive/README.md +29 -29
- data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -9
- data/examples/profile-sensitive/controls/sensitive.rb +9 -9
- data/examples/profile-sensitive/inspec.yml +8 -8
- data/examples/profile/README.md +48 -48
- data/examples/profile/controls/example.rb +23 -23
- data/examples/profile/controls/gordon.rb +36 -36
- data/examples/profile/controls/meta.rb +34 -34
- data/examples/profile/inspec.yml +10 -10
- data/examples/profile/libraries/gordon_config.rb +53 -53
- data/inspec.gemspec +47 -47
- data/lib/bundles/README.md +3 -3
- data/lib/bundles/inspec-artifact.rb +7 -7
- data/lib/bundles/inspec-artifact/README.md +1 -1
- data/lib/bundles/inspec-artifact/cli.rb +277 -277
- data/lib/bundles/inspec-compliance.rb +16 -16
- data/lib/bundles/inspec-compliance/.kitchen.yml +20 -20
- data/lib/bundles/inspec-compliance/README.md +185 -185
- data/lib/bundles/inspec-compliance/api.rb +316 -316
- data/lib/bundles/inspec-compliance/api/login.rb +152 -152
- data/lib/bundles/inspec-compliance/bootstrap.sh +41 -41
- data/lib/bundles/inspec-compliance/cli.rb +254 -254
- data/lib/bundles/inspec-compliance/configuration.rb +103 -103
- data/lib/bundles/inspec-compliance/http.rb +86 -86
- data/lib/bundles/inspec-compliance/support.rb +36 -36
- data/lib/bundles/inspec-compliance/target.rb +98 -98
- data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +93 -93
- data/lib/bundles/inspec-habitat.rb +12 -12
- data/lib/bundles/inspec-habitat/cli.rb +36 -36
- data/lib/bundles/inspec-habitat/log.rb +10 -10
- data/lib/bundles/inspec-habitat/profile.rb +390 -390
- data/lib/bundles/inspec-init.rb +8 -8
- data/lib/bundles/inspec-init/README.md +31 -31
- data/lib/bundles/inspec-init/cli.rb +97 -97
- data/lib/bundles/inspec-init/templates/profile/README.md +3 -3
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +19 -19
- data/lib/bundles/inspec-init/templates/profile/inspec.yml +8 -8
- data/lib/bundles/inspec-supermarket.rb +13 -13
- data/lib/bundles/inspec-supermarket/README.md +45 -45
- data/lib/bundles/inspec-supermarket/api.rb +84 -84
- data/lib/bundles/inspec-supermarket/cli.rb +73 -73
- data/lib/bundles/inspec-supermarket/target.rb +34 -34
- data/lib/fetchers/git.rb +163 -163
- data/lib/fetchers/local.rb +74 -74
- data/lib/fetchers/mock.rb +35 -35
- data/lib/fetchers/url.rb +204 -204
- data/lib/inspec.rb +24 -24
- data/lib/inspec/archive/tar.rb +29 -29
- data/lib/inspec/archive/zip.rb +19 -19
- data/lib/inspec/backend.rb +92 -92
- data/lib/inspec/base_cli.rb +355 -350
- data/lib/inspec/cached_fetcher.rb +66 -66
- data/lib/inspec/cli.rb +292 -292
- data/lib/inspec/completions/bash.sh.erb +45 -45
- data/lib/inspec/completions/fish.sh.erb +34 -34
- data/lib/inspec/completions/zsh.sh.erb +61 -61
- data/lib/inspec/control_eval_context.rb +179 -179
- data/lib/inspec/dependencies/cache.rb +72 -72
- data/lib/inspec/dependencies/dependency_set.rb +92 -92
- data/lib/inspec/dependencies/lockfile.rb +115 -115
- data/lib/inspec/dependencies/requirement.rb +123 -123
- data/lib/inspec/dependencies/resolver.rb +86 -86
- data/lib/inspec/describe.rb +27 -27
- data/lib/inspec/dsl.rb +66 -66
- data/lib/inspec/dsl_shared.rb +33 -33
- data/lib/inspec/env_printer.rb +157 -157
- data/lib/inspec/errors.rb +13 -13
- data/lib/inspec/exceptions.rb +12 -12
- data/lib/inspec/expect.rb +45 -45
- data/lib/inspec/fetcher.rb +45 -45
- data/lib/inspec/file_provider.rb +275 -275
- data/lib/inspec/formatters.rb +3 -3
- data/lib/inspec/formatters/base.rb +250 -250
- data/lib/inspec/formatters/json_rspec.rb +20 -20
- data/lib/inspec/formatters/show_progress.rb +12 -12
- data/lib/inspec/library_eval_context.rb +58 -58
- data/lib/inspec/log.rb +11 -11
- data/lib/inspec/metadata.rb +247 -247
- data/lib/inspec/method_source.rb +24 -24
- data/lib/inspec/objects.rb +14 -14
- data/lib/inspec/objects/attribute.rb +65 -65
- data/lib/inspec/objects/control.rb +61 -61
- data/lib/inspec/objects/describe.rb +92 -92
- data/lib/inspec/objects/each_loop.rb +36 -36
- data/lib/inspec/objects/list.rb +15 -15
- data/lib/inspec/objects/or_test.rb +40 -40
- data/lib/inspec/objects/ruby_helper.rb +15 -15
- data/lib/inspec/objects/tag.rb +27 -27
- data/lib/inspec/objects/test.rb +87 -87
- data/lib/inspec/objects/value.rb +27 -27
- data/lib/inspec/plugins.rb +60 -60
- data/lib/inspec/plugins/cli.rb +24 -24
- data/lib/inspec/plugins/fetcher.rb +86 -86
- data/lib/inspec/plugins/resource.rb +135 -135
- data/lib/inspec/plugins/secret.rb +15 -15
- data/lib/inspec/plugins/source_reader.rb +40 -40
- data/lib/inspec/polyfill.rb +12 -12
- data/lib/inspec/profile.rb +510 -510
- data/lib/inspec/profile_context.rb +207 -207
- data/lib/inspec/profile_vendor.rb +66 -66
- data/lib/inspec/reporters.rb +54 -50
- data/lib/inspec/reporters/base.rb +24 -24
- data/lib/inspec/reporters/cli.rb +356 -356
- data/lib/inspec/reporters/json.rb +116 -116
- data/lib/inspec/reporters/json_min.rb +48 -48
- data/lib/inspec/reporters/junit.rb +77 -77
- data/lib/inspec/require_loader.rb +33 -33
- data/lib/inspec/resource.rb +186 -186
- data/lib/inspec/rule.rb +266 -266
- data/lib/inspec/runner.rb +345 -345
- data/lib/inspec/runner_mock.rb +41 -41
- data/lib/inspec/runner_rspec.rb +175 -175
- data/lib/inspec/runtime_profile.rb +26 -26
- data/lib/inspec/schema.rb +213 -213
- data/lib/inspec/secrets.rb +19 -19
- data/lib/inspec/secrets/yaml.rb +30 -30
- data/lib/inspec/shell.rb +220 -220
- data/lib/inspec/shell_detector.rb +90 -90
- data/lib/inspec/source_reader.rb +29 -29
- data/lib/inspec/version.rb +8 -8
- data/lib/matchers/matchers.rb +339 -339
- data/lib/resource_support/aws.rb +41 -41
- data/lib/resource_support/aws/aws_backend_base.rb +12 -12
- data/lib/resource_support/aws/aws_backend_factory_mixin.rb +12 -12
- data/lib/resource_support/aws/aws_plural_resource_mixin.rb +21 -21
- data/lib/resource_support/aws/aws_resource_mixin.rb +66 -66
- data/lib/resource_support/aws/aws_singular_resource_mixin.rb +24 -24
- data/lib/resources/aide_conf.rb +159 -160
- data/lib/resources/apache.rb +48 -48
- data/lib/resources/apache_conf.rb +156 -156
- data/lib/resources/apt.rb +149 -149
- data/lib/resources/audit_policy.rb +63 -63
- data/lib/resources/auditd.rb +231 -231
- data/lib/resources/auditd_conf.rb +55 -55
- data/lib/resources/aws/aws_cloudtrail_trail.rb +77 -77
- data/lib/resources/aws/aws_cloudtrail_trails.rb +47 -47
- data/lib/resources/aws/aws_cloudwatch_alarm.rb +62 -62
- data/lib/resources/aws/aws_cloudwatch_log_metric_filter.rb +100 -100
- data/lib/resources/aws/aws_config_recorder.rb +98 -98
- data/lib/resources/aws/aws_ec2_instance.rb +157 -157
- data/lib/resources/aws/aws_iam_access_key.rb +106 -106
- data/lib/resources/aws/aws_iam_access_keys.rb +149 -144
- data/lib/resources/aws/aws_iam_group.rb +56 -56
- data/lib/resources/aws/aws_iam_groups.rb +52 -45
- data/lib/resources/aws/aws_iam_password_policy.rb +116 -116
- data/lib/resources/aws/aws_iam_policies.rb +53 -46
- data/lib/resources/aws/aws_iam_policy.rb +125 -119
- data/lib/resources/aws/aws_iam_role.rb +51 -51
- data/lib/resources/aws/aws_iam_root_user.rb +60 -60
- data/lib/resources/aws/aws_iam_user.rb +111 -111
- data/lib/resources/aws/aws_iam_users.rb +108 -96
- data/lib/resources/aws/aws_kms_keys.rb +53 -46
- data/lib/resources/aws/aws_route_table.rb +61 -61
- data/lib/resources/aws/aws_s3_bucket.rb +115 -115
- data/lib/resources/aws/aws_security_group.rb +93 -93
- data/lib/resources/aws/aws_security_groups.rb +68 -68
- data/lib/resources/aws/aws_sns_topic.rb +53 -53
- data/lib/resources/aws/aws_subnet.rb +88 -88
- data/lib/resources/aws/aws_subnets.rb +53 -53
- data/lib/resources/aws/aws_vpc.rb +69 -69
- data/lib/resources/aws/aws_vpcs.rb +45 -45
- data/lib/resources/azure/azure_backend.rb +377 -377
- data/lib/resources/azure/azure_generic_resource.rb +59 -59
- data/lib/resources/azure/azure_resource_group.rb +152 -152
- data/lib/resources/azure/azure_virtual_machine.rb +264 -264
- data/lib/resources/azure/azure_virtual_machine_data_disk.rb +136 -136
- data/lib/resources/bash.rb +35 -35
- data/lib/resources/bond.rb +68 -68
- data/lib/resources/bridge.rb +122 -122
- data/lib/resources/command.rb +73 -69
- data/lib/resources/cpan.rb +58 -58
- data/lib/resources/cran.rb +64 -64
- data/lib/resources/crontab.rb +169 -170
- data/lib/resources/csv.rb +60 -60
- data/lib/resources/dh_params.rb +82 -82
- data/lib/resources/directory.rb +25 -25
- data/lib/resources/docker.rb +236 -236
- data/lib/resources/docker_container.rb +89 -89
- data/lib/resources/docker_image.rb +83 -83
- data/lib/resources/docker_object.rb +57 -57
- data/lib/resources/docker_service.rb +90 -90
- data/lib/resources/elasticsearch.rb +169 -169
- data/lib/resources/etc_fstab.rb +101 -102
- data/lib/resources/etc_group.rb +152 -156
- data/lib/resources/etc_hosts.rb +82 -81
- data/lib/resources/etc_hosts_allow_deny.rb +122 -123
- data/lib/resources/file.rb +298 -298
- data/lib/resources/filesystem.rb +31 -31
- data/lib/resources/firewalld.rb +143 -144
- data/lib/resources/gem.rb +70 -70
- data/lib/resources/groups.rb +215 -215
- data/lib/resources/grub_conf.rb +237 -237
- data/lib/resources/host.rb +306 -300
- data/lib/resources/http.rb +251 -250
- data/lib/resources/iis_app.rb +101 -104
- data/lib/resources/iis_site.rb +148 -148
- data/lib/resources/inetd_conf.rb +62 -62
- data/lib/resources/ini.rb +29 -29
- data/lib/resources/interface.rb +129 -129
- data/lib/resources/iptables.rb +80 -69
- data/lib/resources/json.rb +117 -117
- data/lib/resources/kernel_module.rb +107 -107
- data/lib/resources/kernel_parameter.rb +58 -58
- data/lib/resources/key_rsa.rb +67 -67
- data/lib/resources/limits_conf.rb +55 -55
- data/lib/resources/login_def.rb +66 -66
- data/lib/resources/mount.rb +88 -88
- data/lib/resources/mssql_session.rb +101 -101
- data/lib/resources/mysql.rb +81 -81
- data/lib/resources/mysql_conf.rb +134 -134
- data/lib/resources/mysql_session.rb +71 -71
- data/lib/resources/nginx.rb +96 -96
- data/lib/resources/nginx_conf.rb +227 -227
- data/lib/resources/npm.rb +48 -48
- data/lib/resources/ntp_conf.rb +58 -58
- data/lib/resources/oneget.rb +71 -71
- data/lib/resources/oracledb_session.rb +139 -139
- data/lib/resources/os.rb +36 -36
- data/lib/resources/os_env.rb +76 -76
- data/lib/resources/package.rb +370 -370
- data/lib/resources/packages.rb +111 -111
- data/lib/resources/parse_config.rb +116 -116
- data/lib/resources/passwd.rb +74 -74
- data/lib/resources/pip.rb +89 -89
- data/lib/resources/platform.rb +109 -109
- data/lib/resources/port.rb +771 -771
- data/lib/resources/postgres.rb +130 -130
- data/lib/resources/postgres_conf.rb +121 -121
- data/lib/resources/postgres_hba_conf.rb +99 -100
- data/lib/resources/postgres_ident_conf.rb +76 -78
- data/lib/resources/postgres_session.rb +71 -71
- data/lib/resources/powershell.rb +53 -57
- data/lib/resources/processes.rb +204 -204
- data/lib/resources/rabbitmq_conf.rb +52 -52
- data/lib/resources/registry_key.rb +296 -296
- data/lib/resources/security_policy.rb +180 -180
- data/lib/resources/service.rb +789 -789
- data/lib/resources/shadow.rb +146 -140
- data/lib/resources/ssh_conf.rb +102 -102
- data/lib/resources/ssl.rb +99 -99
- data/lib/resources/sys_info.rb +28 -28
- data/lib/resources/toml.rb +32 -32
- data/lib/resources/users.rb +654 -654
- data/lib/resources/vbscript.rb +68 -69
- data/lib/resources/virtualization.rb +247 -247
- data/lib/resources/windows_feature.rb +84 -84
- data/lib/resources/windows_hotfix.rb +35 -35
- data/lib/resources/windows_task.rb +102 -105
- data/lib/resources/wmi.rb +110 -113
- data/lib/resources/x509_certificate.rb +143 -143
- data/lib/resources/xinetd.rb +111 -111
- data/lib/resources/xml.rb +46 -46
- data/lib/resources/yaml.rb +47 -47
- data/lib/resources/yum.rb +180 -180
- data/lib/resources/zfs_dataset.rb +60 -60
- data/lib/resources/zfs_pool.rb +49 -49
- data/lib/source_readers/flat.rb +39 -39
- data/lib/source_readers/inspec.rb +75 -75
- data/lib/utils/command_wrapper.rb +27 -27
- data/lib/utils/convert.rb +12 -12
- data/lib/utils/database_helpers.rb +77 -77
- data/lib/utils/erlang_parser.rb +192 -192
- data/lib/utils/filter.rb +272 -272
- data/lib/utils/filter_array.rb +27 -27
- data/lib/utils/find_files.rb +44 -44
- data/lib/utils/hash.rb +41 -41
- data/lib/utils/json_log.rb +18 -18
- data/lib/utils/latest_version.rb +22 -22
- data/lib/utils/modulator.rb +12 -12
- data/lib/utils/nginx_parser.rb +85 -85
- data/lib/utils/object_traversal.rb +49 -49
- data/lib/utils/parser.rb +274 -274
- data/lib/utils/plugin_registry.rb +93 -93
- data/lib/utils/simpleconfig.rb +120 -120
- data/lib/utils/spdx.rb +13 -13
- data/lib/utils/spdx.txt +343 -343
- metadata +2 -2
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
# author: Dominik Richter
|
|
3
|
-
# author: Christoph Hartmann
|
|
4
|
-
|
|
5
|
-
require 'utils/plugin_registry'
|
|
6
|
-
|
|
7
|
-
module Inspec
|
|
8
|
-
module Plugins
|
|
9
|
-
class Secret < PluginRegistry::Plugin
|
|
10
|
-
def self.plugin_registry
|
|
11
|
-
Inspec::SecretsBackend
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Dominik Richter
|
|
3
|
+
# author: Christoph Hartmann
|
|
4
|
+
|
|
5
|
+
require 'utils/plugin_registry'
|
|
6
|
+
|
|
7
|
+
module Inspec
|
|
8
|
+
module Plugins
|
|
9
|
+
class Secret < PluginRegistry::Plugin
|
|
10
|
+
def self.plugin_registry
|
|
11
|
+
Inspec::SecretsBackend
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
# author: Dominik Richter
|
|
3
|
-
# author: Christoph Hartmann
|
|
4
|
-
|
|
5
|
-
require 'utils/plugin_registry'
|
|
6
|
-
|
|
7
|
-
module Inspec
|
|
8
|
-
module Plugins
|
|
9
|
-
class SourceReader < PluginRegistry::Plugin
|
|
10
|
-
def self.plugin_registry
|
|
11
|
-
Inspec::SourceReader
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Retrieve this profile's metadata.
|
|
15
|
-
#
|
|
16
|
-
# @return [Inspec::Metadata] profile metadata
|
|
17
|
-
def metadata
|
|
18
|
-
raise "SourceReader #{self} does not implement `metadata()`. This method is required"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Retrieve this profile's tests
|
|
22
|
-
#
|
|
23
|
-
# "tests" here refers to a test file. Individual controls and anonymous
|
|
24
|
-
# tests are later extracted from the raw contents of a test file. The map
|
|
25
|
-
# her simply maps from a test file name to the file contents.
|
|
26
|
-
#
|
|
27
|
-
# @return [Hash] Collection with references pointing to test contents
|
|
28
|
-
def tests
|
|
29
|
-
raise "SourceReader #{self} does not implement `tests()`. This method is required"
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Retrieve this profile's libraries
|
|
33
|
-
#
|
|
34
|
-
# @return [Hash] Collection with references pointing to library contents
|
|
35
|
-
def libraries
|
|
36
|
-
raise "SourceReader #{self} does not implement `libraries()`. This method is required"
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Dominik Richter
|
|
3
|
+
# author: Christoph Hartmann
|
|
4
|
+
|
|
5
|
+
require 'utils/plugin_registry'
|
|
6
|
+
|
|
7
|
+
module Inspec
|
|
8
|
+
module Plugins
|
|
9
|
+
class SourceReader < PluginRegistry::Plugin
|
|
10
|
+
def self.plugin_registry
|
|
11
|
+
Inspec::SourceReader
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Retrieve this profile's metadata.
|
|
15
|
+
#
|
|
16
|
+
# @return [Inspec::Metadata] profile metadata
|
|
17
|
+
def metadata
|
|
18
|
+
raise "SourceReader #{self} does not implement `metadata()`. This method is required"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Retrieve this profile's tests
|
|
22
|
+
#
|
|
23
|
+
# "tests" here refers to a test file. Individual controls and anonymous
|
|
24
|
+
# tests are later extracted from the raw contents of a test file. The map
|
|
25
|
+
# her simply maps from a test file name to the file contents.
|
|
26
|
+
#
|
|
27
|
+
# @return [Hash] Collection with references pointing to test contents
|
|
28
|
+
def tests
|
|
29
|
+
raise "SourceReader #{self} does not implement `tests()`. This method is required"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Retrieve this profile's libraries
|
|
33
|
+
#
|
|
34
|
+
# @return [Hash] Collection with references pointing to library contents
|
|
35
|
+
def libraries
|
|
36
|
+
raise "SourceReader #{self} does not implement `libraries()`. This method is required"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/inspec/polyfill.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
# copyright: 2016, Chef Software Inc.
|
|
3
|
-
# author: Dominik Richter
|
|
4
|
-
# author: Christoph Hartmann
|
|
5
|
-
|
|
6
|
-
class Struct
|
|
7
|
-
unless instance_methods.include? :to_h
|
|
8
|
-
def to_h
|
|
9
|
-
Hash[each_pair.to_a]
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# copyright: 2016, Chef Software Inc.
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
# author: Christoph Hartmann
|
|
5
|
+
|
|
6
|
+
class Struct
|
|
7
|
+
unless instance_methods.include? :to_h
|
|
8
|
+
def to_h
|
|
9
|
+
Hash[each_pair.to_a]
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/inspec/profile.rb
CHANGED
|
@@ -1,510 +1,510 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
# Copyright 2015 Dominik Richter
|
|
3
|
-
# author: Dominik Richter
|
|
4
|
-
# author: Christoph Hartmann
|
|
5
|
-
|
|
6
|
-
require 'forwardable'
|
|
7
|
-
require 'openssl'
|
|
8
|
-
require 'inspec/polyfill'
|
|
9
|
-
require 'inspec/cached_fetcher'
|
|
10
|
-
require 'inspec/file_provider'
|
|
11
|
-
require 'inspec/source_reader'
|
|
12
|
-
require 'inspec/metadata'
|
|
13
|
-
require 'inspec/backend'
|
|
14
|
-
require 'inspec/rule'
|
|
15
|
-
require 'inspec/log'
|
|
16
|
-
require 'inspec/profile_context'
|
|
17
|
-
require 'inspec/runtime_profile'
|
|
18
|
-
require 'inspec/method_source'
|
|
19
|
-
require 'inspec/dependencies/cache'
|
|
20
|
-
require 'inspec/dependencies/lockfile'
|
|
21
|
-
require 'inspec/dependencies/dependency_set'
|
|
22
|
-
|
|
23
|
-
module Inspec
|
|
24
|
-
class Profile
|
|
25
|
-
extend Forwardable
|
|
26
|
-
|
|
27
|
-
def self.resolve_target(target, cache)
|
|
28
|
-
Inspec::Log.debug "Resolve #{target} into cache #{cache.path}"
|
|
29
|
-
Inspec::CachedFetcher.new(target, cache)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Check if the profile contains a vendored cache, move content into global cache
|
|
33
|
-
# TODO: use relative file provider
|
|
34
|
-
# TODO: use source reader for Cache as well
|
|
35
|
-
def self.copy_deps_into_cache(file_provider, opts)
|
|
36
|
-
# filter content
|
|
37
|
-
cache = file_provider.files.find_all do |entry|
|
|
38
|
-
entry.start_with?('vendor')
|
|
39
|
-
end
|
|
40
|
-
content = Hash[cache.map { |x| [x, file_provider.binread(x)] }]
|
|
41
|
-
keys = content.keys
|
|
42
|
-
keys.each do |key|
|
|
43
|
-
next if content[key].nil?
|
|
44
|
-
# remove prefix
|
|
45
|
-
rel = Pathname.new(key).relative_path_from(Pathname.new('vendor')).to_s
|
|
46
|
-
tar = Pathname.new(opts[:vendor_cache].path).join(rel)
|
|
47
|
-
|
|
48
|
-
FileUtils.mkdir_p tar.dirname.to_s
|
|
49
|
-
Inspec::Log.debug "Copy #{tar} to cache directory"
|
|
50
|
-
File.binwrite(tar.to_s, content[key])
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def self.for_path(path, opts)
|
|
55
|
-
file_provider = FileProvider.for_path(path)
|
|
56
|
-
rp = file_provider.relative_provider
|
|
57
|
-
|
|
58
|
-
# copy embedded dependecies into global cache
|
|
59
|
-
copy_deps_into_cache(rp, opts) unless opts[:vendor_cache].nil?
|
|
60
|
-
|
|
61
|
-
reader = Inspec::SourceReader.resolve(rp)
|
|
62
|
-
if reader.nil?
|
|
63
|
-
raise("Don't understand inspec profile in #{path}, it " \
|
|
64
|
-
"doesn't look like a supported profile structure.")
|
|
65
|
-
end
|
|
66
|
-
new(reader, opts)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def self.for_fetcher(fetcher, opts)
|
|
70
|
-
opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
|
|
71
|
-
path, writable = fetcher.fetch
|
|
72
|
-
for_path(path, opts.merge(target: fetcher.target, writable: writable))
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def self.for_target(target, opts = {})
|
|
76
|
-
opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
|
|
77
|
-
fetcher = resolve_target(target, opts[:vendor_cache])
|
|
78
|
-
for_fetcher(fetcher, opts)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
attr_reader :source_reader, :backend, :runner_context, :check_mode
|
|
82
|
-
def_delegator :@source_reader, :tests
|
|
83
|
-
def_delegator :@source_reader, :libraries
|
|
84
|
-
def_delegator :@source_reader, :metadata
|
|
85
|
-
|
|
86
|
-
# rubocop:disable Metrics/AbcSize
|
|
87
|
-
def initialize(source_reader, options = {})
|
|
88
|
-
@source_reader = source_reader
|
|
89
|
-
@target = options[:target]
|
|
90
|
-
@logger = options[:logger] || Logger.new(nil)
|
|
91
|
-
@locked_dependencies = options[:dependencies]
|
|
92
|
-
@controls = options[:controls] || []
|
|
93
|
-
@writable = options[:writable] || false
|
|
94
|
-
@profile_id = options[:id]
|
|
95
|
-
@cache = options[:vendor_cache] || Cache.new
|
|
96
|
-
@attr_values = options[:attributes]
|
|
97
|
-
@tests_collected = false
|
|
98
|
-
@libraries_loaded = false
|
|
99
|
-
@check_mode = options[:check_mode] || false
|
|
100
|
-
Metadata.finalize(@source_reader.metadata, @profile_id, options)
|
|
101
|
-
|
|
102
|
-
# if a backend has already been created, clone it so each profile has its own unique backend object
|
|
103
|
-
# otherwise, create a new backend object
|
|
104
|
-
#
|
|
105
|
-
# This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec`
|
|
106
|
-
# with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if
|
|
107
|
-
# we share the backend between profiles.
|
|
108
|
-
#
|
|
109
|
-
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
|
110
|
-
train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
|
|
111
|
-
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
|
|
112
|
-
@runtime_profile = RuntimeProfile.new(self)
|
|
113
|
-
@backend.profile = @runtime_profile
|
|
114
|
-
|
|
115
|
-
@runner_context =
|
|
116
|
-
options[:profile_context] ||
|
|
117
|
-
Inspec::ProfileContext.for_profile(self, @backend, @attr_values)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def name
|
|
121
|
-
metadata.params[:name]
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def version
|
|
125
|
-
metadata.params[:version]
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def writable?
|
|
129
|
-
@writable
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
#
|
|
133
|
-
# Is this profile is supported on the current platform of the
|
|
134
|
-
# backend machine and the current inspec version.
|
|
135
|
-
#
|
|
136
|
-
# @returns [TrueClass, FalseClass]
|
|
137
|
-
#
|
|
138
|
-
def supported?
|
|
139
|
-
supports_platform? && supports_runtime?
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def supports_platform?
|
|
143
|
-
if @supports_platform.nil?
|
|
144
|
-
@supports_platform = metadata.supports_platform?(@backend)
|
|
145
|
-
end
|
|
146
|
-
@supports_platform
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def supports_runtime?
|
|
150
|
-
if @supports_runtime.nil?
|
|
151
|
-
@supports_runtime = metadata.supports_runtime?
|
|
152
|
-
end
|
|
153
|
-
@supports_runtime
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def params
|
|
157
|
-
@params ||= load_params
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def collect_tests(include_list = @controls)
|
|
161
|
-
if !@tests_collected
|
|
162
|
-
locked_dependencies.each(&:collect_tests)
|
|
163
|
-
|
|
164
|
-
tests.each do |path, content|
|
|
165
|
-
next if content.nil? || content.empty?
|
|
166
|
-
abs_path = source_reader.target.abs_path(path)
|
|
167
|
-
@runner_context.load_control_file(content, abs_path, nil)
|
|
168
|
-
end
|
|
169
|
-
@tests_collected = true
|
|
170
|
-
end
|
|
171
|
-
filter_controls(@runner_context.all_rules, include_list)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def filter_controls(controls_array, include_list)
|
|
175
|
-
return controls_array if include_list.nil? || include_list.empty?
|
|
176
|
-
controls_array.select do |c|
|
|
177
|
-
id = ::Inspec::Rule.rule_id(c)
|
|
178
|
-
include_list.include?(id)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def load_libraries
|
|
183
|
-
return @runner_context if @libraries_loaded
|
|
184
|
-
|
|
185
|
-
locked_dependencies.each do |d|
|
|
186
|
-
c = d.load_libraries
|
|
187
|
-
@runner_context.add_resources(c)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
libs = libraries.map do |path, content|
|
|
191
|
-
[content, path]
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
@runner_context.load_libraries(libs)
|
|
195
|
-
@libraries_loaded = true
|
|
196
|
-
@runner_context
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def to_s
|
|
200
|
-
"Inspec::Profile<#{name}>"
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# return info using uncached params
|
|
204
|
-
def info!
|
|
205
|
-
info(load_params.dup)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def info(res = params.dup)
|
|
209
|
-
# add information about the controls
|
|
210
|
-
res[:controls] = res[:controls].map do |id, rule|
|
|
211
|
-
next if id.to_s.empty?
|
|
212
|
-
data = rule.dup
|
|
213
|
-
data.delete(:checks)
|
|
214
|
-
data[:impact] ||= 0.5
|
|
215
|
-
data[:impact] = 1.0 if data[:impact] > 1.0
|
|
216
|
-
data[:impact] = 0.0 if data[:impact] < 0.0
|
|
217
|
-
data[:id] = id
|
|
218
|
-
data
|
|
219
|
-
end.compact
|
|
220
|
-
|
|
221
|
-
# resolve hash structure in groups
|
|
222
|
-
res[:groups] = res[:groups].map do |id, group|
|
|
223
|
-
group[:id] = id
|
|
224
|
-
group
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# add information about the required attributes
|
|
228
|
-
res[:attributes] = res[:attributes].map(&:to_hash) unless res[:attributes].nil? || res[:attributes].empty?
|
|
229
|
-
res[:sha256] = sha256
|
|
230
|
-
res
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# Check if the profile is internally well-structured. The logger will be
|
|
234
|
-
# used to print information on errors and warnings which are found.
|
|
235
|
-
#
|
|
236
|
-
# @return [Boolean] true if no errors were found, false otherwise
|
|
237
|
-
def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
238
|
-
# initial values for response object
|
|
239
|
-
result = {
|
|
240
|
-
summary: {
|
|
241
|
-
valid: false,
|
|
242
|
-
timestamp: Time.now.iso8601,
|
|
243
|
-
location: @target,
|
|
244
|
-
profile: nil,
|
|
245
|
-
controls: 0,
|
|
246
|
-
},
|
|
247
|
-
errors: [],
|
|
248
|
-
warnings: [],
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
entry = lambda { |file, line, column, control, msg|
|
|
252
|
-
{
|
|
253
|
-
file: file,
|
|
254
|
-
line: line,
|
|
255
|
-
column: column,
|
|
256
|
-
control_id: control,
|
|
257
|
-
msg: msg,
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
warn = lambda { |file, line, column, control, msg|
|
|
262
|
-
@logger.warn(msg)
|
|
263
|
-
result[:warnings].push(entry.call(file, line, column, control, msg))
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
error = lambda { |file, line, column, control, msg|
|
|
267
|
-
@logger.error(msg)
|
|
268
|
-
result[:errors].push(entry.call(file, line, column, control, msg))
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
@logger.info "Checking profile in #{@target}"
|
|
272
|
-
meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
|
|
273
|
-
if meta_path =~ /metadata\.rb$/
|
|
274
|
-
warn.call(@target, 0, 0, nil, 'The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# verify metadata
|
|
278
|
-
m_errors, m_warnings = metadata.valid
|
|
279
|
-
m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
|
|
280
|
-
m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
|
|
281
|
-
m_unsupported = metadata.unsupported
|
|
282
|
-
m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") }
|
|
283
|
-
@logger.info 'Metadata OK.' if m_errors.empty? && m_unsupported.empty?
|
|
284
|
-
|
|
285
|
-
# extract profile name
|
|
286
|
-
result[:summary][:profile] = metadata.params[:name]
|
|
287
|
-
|
|
288
|
-
# check if the profile is using the old test directory instead of the
|
|
289
|
-
# new controls directory
|
|
290
|
-
if @source_reader.tests.keys.any? { |x| x =~ %r{^test/$} }
|
|
291
|
-
warn.call(@target, 0, 0, nil, 'Profile uses deprecated `test` directory, rename it to `controls`.')
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
count = controls_count
|
|
295
|
-
result[:summary][:controls] = count
|
|
296
|
-
if count == 0
|
|
297
|
-
warn.call(nil, nil, nil, nil, 'No controls or tests were defined.')
|
|
298
|
-
else
|
|
299
|
-
@logger.info("Found #{count} controls.")
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
# iterate over hash of groups
|
|
303
|
-
params[:controls].each { |id, control|
|
|
304
|
-
sfile = control[:source_location][:ref]
|
|
305
|
-
sline = control[:source_location][:line]
|
|
306
|
-
error.call(sfile, sline, nil, id, 'Avoid controls with empty IDs') if id.nil? or id.empty?
|
|
307
|
-
next if id.start_with? '(generated '
|
|
308
|
-
warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty?
|
|
309
|
-
warn.call(sfile, sline, nil, id, "Control #{id} has no description") if control[:desc].to_s.empty?
|
|
310
|
-
warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
|
|
311
|
-
warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
|
|
312
|
-
warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty?
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
# profile is valid if we could not find any error
|
|
316
|
-
result[:summary][:valid] = result[:errors].empty?
|
|
317
|
-
|
|
318
|
-
@logger.info 'Control definitions OK.' if result[:warnings].empty?
|
|
319
|
-
result
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def controls_count
|
|
323
|
-
params[:controls].values.length
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
# generates a archive of a folder profile
|
|
327
|
-
# assumes that the profile was checked before
|
|
328
|
-
def archive(opts)
|
|
329
|
-
# check if file exists otherwise overwrite the archive
|
|
330
|
-
dst = archive_name(opts)
|
|
331
|
-
if dst.exist? && !opts[:overwrite]
|
|
332
|
-
@logger.info "Archive #{dst} exists already. Use --overwrite."
|
|
333
|
-
return false
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
# remove existing archive
|
|
337
|
-
File.delete(dst) if dst.exist?
|
|
338
|
-
@logger.info "Generate archive #{dst}."
|
|
339
|
-
|
|
340
|
-
# filter files that should not be part of the profile
|
|
341
|
-
# TODO ignore all .files, but add the files to debug output
|
|
342
|
-
|
|
343
|
-
# display all files that will be part of the archive
|
|
344
|
-
@logger.debug 'Add the following files to archive:'
|
|
345
|
-
files.each { |f| @logger.debug ' ' + f }
|
|
346
|
-
|
|
347
|
-
if opts[:zip]
|
|
348
|
-
# generate zip archive
|
|
349
|
-
require 'inspec/archive/zip'
|
|
350
|
-
zag = Inspec::Archive::ZipArchiveGenerator.new
|
|
351
|
-
zag.archive(root_path, files, dst)
|
|
352
|
-
else
|
|
353
|
-
# generate tar archive
|
|
354
|
-
require 'inspec/archive/tar'
|
|
355
|
-
tag = Inspec::Archive::TarArchiveGenerator.new
|
|
356
|
-
tag.archive(root_path, files, dst)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
@logger.info 'Finished archive generation.'
|
|
360
|
-
true
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
def locked_dependencies
|
|
364
|
-
@locked_dependencies ||= load_dependencies
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
def lockfile_exists?
|
|
368
|
-
@source_reader.target.files.include?('inspec.lock')
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
def lockfile_path
|
|
372
|
-
File.join(cwd, 'inspec.lock')
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
def root_path
|
|
376
|
-
@source_reader.target.prefix
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
def files
|
|
380
|
-
@source_reader.target.files
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
#
|
|
384
|
-
# TODO(ssd): Relative path handling really needs to be carefully
|
|
385
|
-
# thought through, especially with respect to relative paths in
|
|
386
|
-
# tarballs.
|
|
387
|
-
#
|
|
388
|
-
def cwd
|
|
389
|
-
@target.is_a?(String) && File.directory?(@target) ? @target : './'
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
def lockfile
|
|
393
|
-
@lockfile ||= if lockfile_exists?
|
|
394
|
-
Inspec::Lockfile.from_content(@source_reader.target.read('inspec.lock'))
|
|
395
|
-
else
|
|
396
|
-
generate_lockfile
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
#
|
|
401
|
-
# Generate an in-memory lockfile. This won't render the lock file
|
|
402
|
-
# to disk, it must be explicitly written to disk by the caller.
|
|
403
|
-
#
|
|
404
|
-
# @param vendor_path [String] Path to the on-disk vendor dir
|
|
405
|
-
# @return [Inspec::Lockfile]
|
|
406
|
-
#
|
|
407
|
-
def generate_lockfile
|
|
408
|
-
res = Inspec::DependencySet.new(cwd, @cache, nil, @backend)
|
|
409
|
-
res.vendor(metadata.dependencies)
|
|
410
|
-
Inspec::Lockfile.from_dependency_set(res)
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
def load_dependencies
|
|
414
|
-
Inspec::DependencySet.from_lockfile(lockfile, cwd, @cache, @backend, { attributes: @attr_values })
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
# Calculate this profile's SHA256 checksum. Includes metadata, dependencies,
|
|
418
|
-
# libraries, data files, and controls.
|
|
419
|
-
#
|
|
420
|
-
# @return [Type] description of returned object
|
|
421
|
-
def sha256
|
|
422
|
-
# get all dependency checksums
|
|
423
|
-
deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
|
|
424
|
-
|
|
425
|
-
res = OpenSSL::Digest::SHA256.new
|
|
426
|
-
files = source_reader.tests.to_a + source_reader.libraries.to_a +
|
|
427
|
-
source_reader.data_files.to_a +
|
|
428
|
-
[['inspec.yml', source_reader.metadata.content]] +
|
|
429
|
-
[['inspec.lock.deps', YAML.dump(deps)]]
|
|
430
|
-
|
|
431
|
-
files.sort_by { |a| a[0] }
|
|
432
|
-
.map { |f| res << f[0] << "\0" << f[1] << "\0" }
|
|
433
|
-
|
|
434
|
-
res.digest.unpack('H*')[0]
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
private
|
|
438
|
-
|
|
439
|
-
# Create an archive name for this profile and an additional options
|
|
440
|
-
# configuration. Either use :output or generate the name from metadata.
|
|
441
|
-
#
|
|
442
|
-
# @param [Hash] configuration options
|
|
443
|
-
# @return [Pathname] path for the archive
|
|
444
|
-
def archive_name(opts)
|
|
445
|
-
if (name = opts[:output])
|
|
446
|
-
return Pathname.new(name)
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
name = params[:name] ||
|
|
450
|
-
raise('Cannot create an archive without a profile name! Please '\
|
|
451
|
-
'specify the name in metadata or use --output to create the archive.')
|
|
452
|
-
version = params[:version] ||
|
|
453
|
-
raise('Cannot create an archive without a profile version! Please '\
|
|
454
|
-
'specify the version in metadata or use --output to create the archive.')
|
|
455
|
-
ext = opts[:zip] ? 'zip' : 'tar.gz'
|
|
456
|
-
slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
|
|
457
|
-
Pathname.new(Dir.pwd).join("#{slug}-#{version}.#{ext}")
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
def load_params
|
|
461
|
-
params = @source_reader.metadata.params
|
|
462
|
-
params[:name] = @profile_id unless @profile_id.nil?
|
|
463
|
-
load_checks_params(params)
|
|
464
|
-
@profile_id ||= params[:name]
|
|
465
|
-
params
|
|
466
|
-
end
|
|
467
|
-
|
|
468
|
-
def load_checks_params(params)
|
|
469
|
-
load_libraries
|
|
470
|
-
tests = collect_tests
|
|
471
|
-
params[:controls] = controls = {}
|
|
472
|
-
params[:groups] = groups = {}
|
|
473
|
-
prefix = @source_reader.target.prefix || ''
|
|
474
|
-
tests.each do |rule|
|
|
475
|
-
next if rule.nil?
|
|
476
|
-
f = load_rule_filepath(prefix, rule)
|
|
477
|
-
load_rule(rule, f, controls, groups)
|
|
478
|
-
end
|
|
479
|
-
params[:attributes] = @runner_context.attributes
|
|
480
|
-
params
|
|
481
|
-
end
|
|
482
|
-
|
|
483
|
-
def load_rule_filepath(prefix, rule)
|
|
484
|
-
file = rule.instance_variable_get(:@__file)
|
|
485
|
-
file = file[prefix.length..-1] if file.start_with?(prefix)
|
|
486
|
-
file
|
|
487
|
-
end
|
|
488
|
-
|
|
489
|
-
def load_rule(rule, file, controls, groups)
|
|
490
|
-
id = Inspec::Rule.rule_id(rule)
|
|
491
|
-
location = rule.instance_variable_get(:@__source_location)
|
|
492
|
-
controls[id] = {
|
|
493
|
-
title: rule.title,
|
|
494
|
-
desc: rule.desc,
|
|
495
|
-
impact: rule.impact,
|
|
496
|
-
refs: rule.ref,
|
|
497
|
-
tags: rule.tag,
|
|
498
|
-
checks: Inspec::Rule.checks(rule),
|
|
499
|
-
code: Inspec::MethodSource.code_at(location, source_reader),
|
|
500
|
-
source_location: location,
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
groups[file] ||= {
|
|
504
|
-
title: rule.instance_variable_get(:@__group_title),
|
|
505
|
-
controls: [],
|
|
506
|
-
}
|
|
507
|
-
groups[file][:controls].push(id)
|
|
508
|
-
end
|
|
509
|
-
end
|
|
510
|
-
end
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# Copyright 2015 Dominik Richter
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
# author: Christoph Hartmann
|
|
5
|
+
|
|
6
|
+
require 'forwardable'
|
|
7
|
+
require 'openssl'
|
|
8
|
+
require 'inspec/polyfill'
|
|
9
|
+
require 'inspec/cached_fetcher'
|
|
10
|
+
require 'inspec/file_provider'
|
|
11
|
+
require 'inspec/source_reader'
|
|
12
|
+
require 'inspec/metadata'
|
|
13
|
+
require 'inspec/backend'
|
|
14
|
+
require 'inspec/rule'
|
|
15
|
+
require 'inspec/log'
|
|
16
|
+
require 'inspec/profile_context'
|
|
17
|
+
require 'inspec/runtime_profile'
|
|
18
|
+
require 'inspec/method_source'
|
|
19
|
+
require 'inspec/dependencies/cache'
|
|
20
|
+
require 'inspec/dependencies/lockfile'
|
|
21
|
+
require 'inspec/dependencies/dependency_set'
|
|
22
|
+
|
|
23
|
+
module Inspec
|
|
24
|
+
class Profile
|
|
25
|
+
extend Forwardable
|
|
26
|
+
|
|
27
|
+
def self.resolve_target(target, cache)
|
|
28
|
+
Inspec::Log.debug "Resolve #{target} into cache #{cache.path}"
|
|
29
|
+
Inspec::CachedFetcher.new(target, cache)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Check if the profile contains a vendored cache, move content into global cache
|
|
33
|
+
# TODO: use relative file provider
|
|
34
|
+
# TODO: use source reader for Cache as well
|
|
35
|
+
def self.copy_deps_into_cache(file_provider, opts)
|
|
36
|
+
# filter content
|
|
37
|
+
cache = file_provider.files.find_all do |entry|
|
|
38
|
+
entry.start_with?('vendor')
|
|
39
|
+
end
|
|
40
|
+
content = Hash[cache.map { |x| [x, file_provider.binread(x)] }]
|
|
41
|
+
keys = content.keys
|
|
42
|
+
keys.each do |key|
|
|
43
|
+
next if content[key].nil?
|
|
44
|
+
# remove prefix
|
|
45
|
+
rel = Pathname.new(key).relative_path_from(Pathname.new('vendor')).to_s
|
|
46
|
+
tar = Pathname.new(opts[:vendor_cache].path).join(rel)
|
|
47
|
+
|
|
48
|
+
FileUtils.mkdir_p tar.dirname.to_s
|
|
49
|
+
Inspec::Log.debug "Copy #{tar} to cache directory"
|
|
50
|
+
File.binwrite(tar.to_s, content[key])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.for_path(path, opts)
|
|
55
|
+
file_provider = FileProvider.for_path(path)
|
|
56
|
+
rp = file_provider.relative_provider
|
|
57
|
+
|
|
58
|
+
# copy embedded dependecies into global cache
|
|
59
|
+
copy_deps_into_cache(rp, opts) unless opts[:vendor_cache].nil?
|
|
60
|
+
|
|
61
|
+
reader = Inspec::SourceReader.resolve(rp)
|
|
62
|
+
if reader.nil?
|
|
63
|
+
raise("Don't understand inspec profile in #{path}, it " \
|
|
64
|
+
"doesn't look like a supported profile structure.")
|
|
65
|
+
end
|
|
66
|
+
new(reader, opts)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.for_fetcher(fetcher, opts)
|
|
70
|
+
opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
|
|
71
|
+
path, writable = fetcher.fetch
|
|
72
|
+
for_path(path, opts.merge(target: fetcher.target, writable: writable))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.for_target(target, opts = {})
|
|
76
|
+
opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
|
|
77
|
+
fetcher = resolve_target(target, opts[:vendor_cache])
|
|
78
|
+
for_fetcher(fetcher, opts)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
attr_reader :source_reader, :backend, :runner_context, :check_mode
|
|
82
|
+
def_delegator :@source_reader, :tests
|
|
83
|
+
def_delegator :@source_reader, :libraries
|
|
84
|
+
def_delegator :@source_reader, :metadata
|
|
85
|
+
|
|
86
|
+
# rubocop:disable Metrics/AbcSize
|
|
87
|
+
def initialize(source_reader, options = {})
|
|
88
|
+
@source_reader = source_reader
|
|
89
|
+
@target = options[:target]
|
|
90
|
+
@logger = options[:logger] || Logger.new(nil)
|
|
91
|
+
@locked_dependencies = options[:dependencies]
|
|
92
|
+
@controls = options[:controls] || []
|
|
93
|
+
@writable = options[:writable] || false
|
|
94
|
+
@profile_id = options[:id]
|
|
95
|
+
@cache = options[:vendor_cache] || Cache.new
|
|
96
|
+
@attr_values = options[:attributes]
|
|
97
|
+
@tests_collected = false
|
|
98
|
+
@libraries_loaded = false
|
|
99
|
+
@check_mode = options[:check_mode] || false
|
|
100
|
+
Metadata.finalize(@source_reader.metadata, @profile_id, options)
|
|
101
|
+
|
|
102
|
+
# if a backend has already been created, clone it so each profile has its own unique backend object
|
|
103
|
+
# otherwise, create a new backend object
|
|
104
|
+
#
|
|
105
|
+
# This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec`
|
|
106
|
+
# with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if
|
|
107
|
+
# we share the backend between profiles.
|
|
108
|
+
#
|
|
109
|
+
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
|
110
|
+
train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
|
|
111
|
+
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
|
|
112
|
+
@runtime_profile = RuntimeProfile.new(self)
|
|
113
|
+
@backend.profile = @runtime_profile
|
|
114
|
+
|
|
115
|
+
@runner_context =
|
|
116
|
+
options[:profile_context] ||
|
|
117
|
+
Inspec::ProfileContext.for_profile(self, @backend, @attr_values)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def name
|
|
121
|
+
metadata.params[:name]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def version
|
|
125
|
+
metadata.params[:version]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def writable?
|
|
129
|
+
@writable
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Is this profile is supported on the current platform of the
|
|
134
|
+
# backend machine and the current inspec version.
|
|
135
|
+
#
|
|
136
|
+
# @returns [TrueClass, FalseClass]
|
|
137
|
+
#
|
|
138
|
+
def supported?
|
|
139
|
+
supports_platform? && supports_runtime?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def supports_platform?
|
|
143
|
+
if @supports_platform.nil?
|
|
144
|
+
@supports_platform = metadata.supports_platform?(@backend)
|
|
145
|
+
end
|
|
146
|
+
@supports_platform
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def supports_runtime?
|
|
150
|
+
if @supports_runtime.nil?
|
|
151
|
+
@supports_runtime = metadata.supports_runtime?
|
|
152
|
+
end
|
|
153
|
+
@supports_runtime
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def params
|
|
157
|
+
@params ||= load_params
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def collect_tests(include_list = @controls)
|
|
161
|
+
if !@tests_collected
|
|
162
|
+
locked_dependencies.each(&:collect_tests)
|
|
163
|
+
|
|
164
|
+
tests.each do |path, content|
|
|
165
|
+
next if content.nil? || content.empty?
|
|
166
|
+
abs_path = source_reader.target.abs_path(path)
|
|
167
|
+
@runner_context.load_control_file(content, abs_path, nil)
|
|
168
|
+
end
|
|
169
|
+
@tests_collected = true
|
|
170
|
+
end
|
|
171
|
+
filter_controls(@runner_context.all_rules, include_list)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def filter_controls(controls_array, include_list)
|
|
175
|
+
return controls_array if include_list.nil? || include_list.empty?
|
|
176
|
+
controls_array.select do |c|
|
|
177
|
+
id = ::Inspec::Rule.rule_id(c)
|
|
178
|
+
include_list.include?(id)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def load_libraries
|
|
183
|
+
return @runner_context if @libraries_loaded
|
|
184
|
+
|
|
185
|
+
locked_dependencies.each do |d|
|
|
186
|
+
c = d.load_libraries
|
|
187
|
+
@runner_context.add_resources(c)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
libs = libraries.map do |path, content|
|
|
191
|
+
[content, path]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
@runner_context.load_libraries(libs)
|
|
195
|
+
@libraries_loaded = true
|
|
196
|
+
@runner_context
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def to_s
|
|
200
|
+
"Inspec::Profile<#{name}>"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# return info using uncached params
|
|
204
|
+
def info!
|
|
205
|
+
info(load_params.dup)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def info(res = params.dup)
|
|
209
|
+
# add information about the controls
|
|
210
|
+
res[:controls] = res[:controls].map do |id, rule|
|
|
211
|
+
next if id.to_s.empty?
|
|
212
|
+
data = rule.dup
|
|
213
|
+
data.delete(:checks)
|
|
214
|
+
data[:impact] ||= 0.5
|
|
215
|
+
data[:impact] = 1.0 if data[:impact] > 1.0
|
|
216
|
+
data[:impact] = 0.0 if data[:impact] < 0.0
|
|
217
|
+
data[:id] = id
|
|
218
|
+
data
|
|
219
|
+
end.compact
|
|
220
|
+
|
|
221
|
+
# resolve hash structure in groups
|
|
222
|
+
res[:groups] = res[:groups].map do |id, group|
|
|
223
|
+
group[:id] = id
|
|
224
|
+
group
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# add information about the required attributes
|
|
228
|
+
res[:attributes] = res[:attributes].map(&:to_hash) unless res[:attributes].nil? || res[:attributes].empty?
|
|
229
|
+
res[:sha256] = sha256
|
|
230
|
+
res
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Check if the profile is internally well-structured. The logger will be
|
|
234
|
+
# used to print information on errors and warnings which are found.
|
|
235
|
+
#
|
|
236
|
+
# @return [Boolean] true if no errors were found, false otherwise
|
|
237
|
+
def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
238
|
+
# initial values for response object
|
|
239
|
+
result = {
|
|
240
|
+
summary: {
|
|
241
|
+
valid: false,
|
|
242
|
+
timestamp: Time.now.iso8601,
|
|
243
|
+
location: @target,
|
|
244
|
+
profile: nil,
|
|
245
|
+
controls: 0,
|
|
246
|
+
},
|
|
247
|
+
errors: [],
|
|
248
|
+
warnings: [],
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
entry = lambda { |file, line, column, control, msg|
|
|
252
|
+
{
|
|
253
|
+
file: file,
|
|
254
|
+
line: line,
|
|
255
|
+
column: column,
|
|
256
|
+
control_id: control,
|
|
257
|
+
msg: msg,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
warn = lambda { |file, line, column, control, msg|
|
|
262
|
+
@logger.warn(msg)
|
|
263
|
+
result[:warnings].push(entry.call(file, line, column, control, msg))
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
error = lambda { |file, line, column, control, msg|
|
|
267
|
+
@logger.error(msg)
|
|
268
|
+
result[:errors].push(entry.call(file, line, column, control, msg))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@logger.info "Checking profile in #{@target}"
|
|
272
|
+
meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
|
|
273
|
+
if meta_path =~ /metadata\.rb$/
|
|
274
|
+
warn.call(@target, 0, 0, nil, 'The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# verify metadata
|
|
278
|
+
m_errors, m_warnings = metadata.valid
|
|
279
|
+
m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
|
|
280
|
+
m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
|
|
281
|
+
m_unsupported = metadata.unsupported
|
|
282
|
+
m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") }
|
|
283
|
+
@logger.info 'Metadata OK.' if m_errors.empty? && m_unsupported.empty?
|
|
284
|
+
|
|
285
|
+
# extract profile name
|
|
286
|
+
result[:summary][:profile] = metadata.params[:name]
|
|
287
|
+
|
|
288
|
+
# check if the profile is using the old test directory instead of the
|
|
289
|
+
# new controls directory
|
|
290
|
+
if @source_reader.tests.keys.any? { |x| x =~ %r{^test/$} }
|
|
291
|
+
warn.call(@target, 0, 0, nil, 'Profile uses deprecated `test` directory, rename it to `controls`.')
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
count = controls_count
|
|
295
|
+
result[:summary][:controls] = count
|
|
296
|
+
if count == 0
|
|
297
|
+
warn.call(nil, nil, nil, nil, 'No controls or tests were defined.')
|
|
298
|
+
else
|
|
299
|
+
@logger.info("Found #{count} controls.")
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# iterate over hash of groups
|
|
303
|
+
params[:controls].each { |id, control|
|
|
304
|
+
sfile = control[:source_location][:ref]
|
|
305
|
+
sline = control[:source_location][:line]
|
|
306
|
+
error.call(sfile, sline, nil, id, 'Avoid controls with empty IDs') if id.nil? or id.empty?
|
|
307
|
+
next if id.start_with? '(generated '
|
|
308
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty?
|
|
309
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has no description") if control[:desc].to_s.empty?
|
|
310
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
|
|
311
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
|
|
312
|
+
warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty?
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# profile is valid if we could not find any error
|
|
316
|
+
result[:summary][:valid] = result[:errors].empty?
|
|
317
|
+
|
|
318
|
+
@logger.info 'Control definitions OK.' if result[:warnings].empty?
|
|
319
|
+
result
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def controls_count
|
|
323
|
+
params[:controls].values.length
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# generates a archive of a folder profile
|
|
327
|
+
# assumes that the profile was checked before
|
|
328
|
+
def archive(opts)
|
|
329
|
+
# check if file exists otherwise overwrite the archive
|
|
330
|
+
dst = archive_name(opts)
|
|
331
|
+
if dst.exist? && !opts[:overwrite]
|
|
332
|
+
@logger.info "Archive #{dst} exists already. Use --overwrite."
|
|
333
|
+
return false
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# remove existing archive
|
|
337
|
+
File.delete(dst) if dst.exist?
|
|
338
|
+
@logger.info "Generate archive #{dst}."
|
|
339
|
+
|
|
340
|
+
# filter files that should not be part of the profile
|
|
341
|
+
# TODO ignore all .files, but add the files to debug output
|
|
342
|
+
|
|
343
|
+
# display all files that will be part of the archive
|
|
344
|
+
@logger.debug 'Add the following files to archive:'
|
|
345
|
+
files.each { |f| @logger.debug ' ' + f }
|
|
346
|
+
|
|
347
|
+
if opts[:zip]
|
|
348
|
+
# generate zip archive
|
|
349
|
+
require 'inspec/archive/zip'
|
|
350
|
+
zag = Inspec::Archive::ZipArchiveGenerator.new
|
|
351
|
+
zag.archive(root_path, files, dst)
|
|
352
|
+
else
|
|
353
|
+
# generate tar archive
|
|
354
|
+
require 'inspec/archive/tar'
|
|
355
|
+
tag = Inspec::Archive::TarArchiveGenerator.new
|
|
356
|
+
tag.archive(root_path, files, dst)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
@logger.info 'Finished archive generation.'
|
|
360
|
+
true
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def locked_dependencies
|
|
364
|
+
@locked_dependencies ||= load_dependencies
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def lockfile_exists?
|
|
368
|
+
@source_reader.target.files.include?('inspec.lock')
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def lockfile_path
|
|
372
|
+
File.join(cwd, 'inspec.lock')
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def root_path
|
|
376
|
+
@source_reader.target.prefix
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def files
|
|
380
|
+
@source_reader.target.files
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
#
|
|
384
|
+
# TODO(ssd): Relative path handling really needs to be carefully
|
|
385
|
+
# thought through, especially with respect to relative paths in
|
|
386
|
+
# tarballs.
|
|
387
|
+
#
|
|
388
|
+
def cwd
|
|
389
|
+
@target.is_a?(String) && File.directory?(@target) ? @target : './'
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def lockfile
|
|
393
|
+
@lockfile ||= if lockfile_exists?
|
|
394
|
+
Inspec::Lockfile.from_content(@source_reader.target.read('inspec.lock'))
|
|
395
|
+
else
|
|
396
|
+
generate_lockfile
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
#
|
|
401
|
+
# Generate an in-memory lockfile. This won't render the lock file
|
|
402
|
+
# to disk, it must be explicitly written to disk by the caller.
|
|
403
|
+
#
|
|
404
|
+
# @param vendor_path [String] Path to the on-disk vendor dir
|
|
405
|
+
# @return [Inspec::Lockfile]
|
|
406
|
+
#
|
|
407
|
+
def generate_lockfile
|
|
408
|
+
res = Inspec::DependencySet.new(cwd, @cache, nil, @backend)
|
|
409
|
+
res.vendor(metadata.dependencies)
|
|
410
|
+
Inspec::Lockfile.from_dependency_set(res)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def load_dependencies
|
|
414
|
+
Inspec::DependencySet.from_lockfile(lockfile, cwd, @cache, @backend, { attributes: @attr_values })
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Calculate this profile's SHA256 checksum. Includes metadata, dependencies,
|
|
418
|
+
# libraries, data files, and controls.
|
|
419
|
+
#
|
|
420
|
+
# @return [Type] description of returned object
|
|
421
|
+
def sha256
|
|
422
|
+
# get all dependency checksums
|
|
423
|
+
deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }]
|
|
424
|
+
|
|
425
|
+
res = OpenSSL::Digest::SHA256.new
|
|
426
|
+
files = source_reader.tests.to_a + source_reader.libraries.to_a +
|
|
427
|
+
source_reader.data_files.to_a +
|
|
428
|
+
[['inspec.yml', source_reader.metadata.content]] +
|
|
429
|
+
[['inspec.lock.deps', YAML.dump(deps)]]
|
|
430
|
+
|
|
431
|
+
files.sort_by { |a| a[0] }
|
|
432
|
+
.map { |f| res << f[0] << "\0" << f[1] << "\0" }
|
|
433
|
+
|
|
434
|
+
res.digest.unpack('H*')[0]
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
private
|
|
438
|
+
|
|
439
|
+
# Create an archive name for this profile and an additional options
|
|
440
|
+
# configuration. Either use :output or generate the name from metadata.
|
|
441
|
+
#
|
|
442
|
+
# @param [Hash] configuration options
|
|
443
|
+
# @return [Pathname] path for the archive
|
|
444
|
+
def archive_name(opts)
|
|
445
|
+
if (name = opts[:output])
|
|
446
|
+
return Pathname.new(name)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
name = params[:name] ||
|
|
450
|
+
raise('Cannot create an archive without a profile name! Please '\
|
|
451
|
+
'specify the name in metadata or use --output to create the archive.')
|
|
452
|
+
version = params[:version] ||
|
|
453
|
+
raise('Cannot create an archive without a profile version! Please '\
|
|
454
|
+
'specify the version in metadata or use --output to create the archive.')
|
|
455
|
+
ext = opts[:zip] ? 'zip' : 'tar.gz'
|
|
456
|
+
slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
|
|
457
|
+
Pathname.new(Dir.pwd).join("#{slug}-#{version}.#{ext}")
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def load_params
|
|
461
|
+
params = @source_reader.metadata.params
|
|
462
|
+
params[:name] = @profile_id unless @profile_id.nil?
|
|
463
|
+
load_checks_params(params)
|
|
464
|
+
@profile_id ||= params[:name]
|
|
465
|
+
params
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def load_checks_params(params)
|
|
469
|
+
load_libraries
|
|
470
|
+
tests = collect_tests
|
|
471
|
+
params[:controls] = controls = {}
|
|
472
|
+
params[:groups] = groups = {}
|
|
473
|
+
prefix = @source_reader.target.prefix || ''
|
|
474
|
+
tests.each do |rule|
|
|
475
|
+
next if rule.nil?
|
|
476
|
+
f = load_rule_filepath(prefix, rule)
|
|
477
|
+
load_rule(rule, f, controls, groups)
|
|
478
|
+
end
|
|
479
|
+
params[:attributes] = @runner_context.attributes
|
|
480
|
+
params
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def load_rule_filepath(prefix, rule)
|
|
484
|
+
file = rule.instance_variable_get(:@__file)
|
|
485
|
+
file = file[prefix.length..-1] if file.start_with?(prefix)
|
|
486
|
+
file
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def load_rule(rule, file, controls, groups)
|
|
490
|
+
id = Inspec::Rule.rule_id(rule)
|
|
491
|
+
location = rule.instance_variable_get(:@__source_location)
|
|
492
|
+
controls[id] = {
|
|
493
|
+
title: rule.title,
|
|
494
|
+
desc: rule.desc,
|
|
495
|
+
impact: rule.impact,
|
|
496
|
+
refs: rule.ref,
|
|
497
|
+
tags: rule.tag,
|
|
498
|
+
checks: Inspec::Rule.checks(rule),
|
|
499
|
+
code: Inspec::MethodSource.code_at(location, source_reader),
|
|
500
|
+
source_location: location,
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
groups[file] ||= {
|
|
504
|
+
title: rule.instance_variable_get(:@__group_title),
|
|
505
|
+
controls: [],
|
|
506
|
+
}
|
|
507
|
+
groups[file][:controls].push(id)
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
end
|