Path: blob/master/spec/module_validation_spec.rb
19534 views
RSpec.describe ModuleValidation::Validator do1let(:mod_class) { Msf::Exploit }2let(:mod_options) do3{4framework: framework,5name: 'Testing bad chars',6author: [7Msf::Author.new('Foobar'),8Msf::Author.new('Jim'),9Msf::Author.new('Bob')10],11license: MSF_LICENSE,12references: [Msf::Module::SiteReference.new('URL', 'https://example.com')],13rank_to_s: Msf::RankingName[Msf::ExcellentRanking],14rank: Msf::ExcellentRanking,15notes: {16'Stability' => [Msf::CRASH_SAFE],17'SideEffects' => [Msf::ARTIFACTS_ON_DISK],18'Reliability' => [Msf::FIRST_ATTEMPT_FAIL],19'AKA' => %w[SMBGhost CoronaBlue]20},21stability: [Msf::CRASH_SAFE],22side_effects: [Msf::ARTIFACTS_ON_DISK],23reliability: [Msf::FIRST_ATTEMPT_FAIL],24file_path: 'modules/exploits/windows/smb/cve_2020_0796_smbghost.rb',25type: 'exploit',26platform: Msf::Module::PlatformList.new(Msf::Module::Platform::Windows),27arch: [Rex::Arch::ARCH_X86],28targets: [Msf::Module::Target.new('Windows 10 v1903-1909 x64', { 'Platform' => 'win', 'Arch' => ['x64'] })],29description: %q{30A vulnerability exists within the Microsoft Server Message Block 3.1.1 (SMBv3) protocol that can be leveraged to31execute code on a vulnerable server. This remove exploit implementation leverages this flaw to execute code32in the context of the kernel, finally yielding a session as NT AUTHORITY\SYSTEM in spoolsv.exe. Exploitation33can take a few minutes as the necessary data is gathered.34}35}36end37let(:framework) do38instance_double(Msf::Framework)39end4041let(:mod) do42instance_double(mod_class, **mod_options)43end4445subject { described_class.new(mod) }4647describe '#errors' do48before(:each) do |example|49subject.validate unless example.metadata[:skip_before]50end5152context 'when the module is valid' do53it 'has no errors' do54expect(subject.errors.full_messages).to be_empty55end56end5758context 'when notes contains an invalid value' do59let(:mod_options) do60super().merge(notes: {61'Stability' => [Msf::CRASH_SAFE],62'SideEffects' => [Msf::ARTIFACTS_ON_DISK],63'Reliability' => [Msf::FIRST_ATTEMPT_FAIL],64'AKA' => %w[SMBGhost CoronaBlue],65'NOCVE' => 'Reason not given'66})67end6869it 'has errors' do70expect(subject.errors.full_messages).to eq ['Notes note value "NOCVE" must be an array, got "Reason not given"']71end72end7374context 'when the stability rating contains an invalid value' do75let(:mod_options) do76super().merge(stability: ['CRASH_SAFE'], rank: Msf::GreatRanking, rank_to_s: 'great')77end7879it 'has errors' do80expect(subject.errors.full_messages).to eq ['Stability contains invalid values ["CRASH_SAFE"] - only ["crash-safe", "crash-service-restarts", "crash-service-down", "crash-os-restarts", "crash-os-down", "service-resource-loss", "os-resource-loss"] is allowed']81end82end8384context 'when the stability rating contains an invalid values and an excellent ranking' do85let(:mod_options) do86super().merge(stability: [Msf::CRASH_SERVICE_RESTARTS])87end8889it 'has errors' do90expect(subject.errors.full_messages).to eq ['Stability must have CRASH_SAFE value if module has an ExcellentRanking, instead found ["crash-service-restarts"]']91end92end9394context 'when the side effects rating contains an invalid value' do95let(:mod_options) do96super().merge(side_effects: ['ARTIFACTS_ON_DISK'])97end9899it 'has errors' do100expect(subject.errors.full_messages).to eq ['Side effects contains invalid values ["ARTIFACTS_ON_DISK"] - only ["artifacts-on-disk", "config-changes", "ioc-in-logs", "account-lockouts", "account-logout", "screen-effects", "audio-effects", "physical-effects"] is allowed']101end102end103104context 'when the reliability rating contains an invalid value' do105let(:mod_options) do106super().merge(reliability: ['FIRST_ATTEMPT_FAIL'])107end108109it 'has errors' do110expect(subject.errors.full_messages).to eq ['Reliability contains invalid values ["FIRST_ATTEMPT_FAIL"] - only ["first-attempt-fail", "repeatable-session", "unreliable-session", "event-dependent"] is allowed']111end112end113114context 'when the references contains an invalid value' do115let(:mod_options) do116super().merge(references: [117Msf::Module::SiteReference.new('url', 'https://example.com'),118Msf::Module::SiteReference.new('FOO', 'https://example.com'),119Msf::Module::SiteReference.new('NOCVE', 'Reason not given'),120Msf::Module::SiteReference.new('AKA', 'Foobar'),121Msf::Module::SiteReference.new('ATTACK', 'Foobar'),122])123end124125it 'has errors' do126expect(subject.errors.full_messages).to eq [127"References url is not valid, must be in [\"ATT&CK\", \"CVE\", \"CWE\", \"BID\", \"MSB\", \"EDB\", \"US-CERT-VU\", \"ZDI\", \"URL\", \"WPVDB\", \"PACKETSTORM\", \"LOGO\", \"SOUNDTRACK\", \"OSVDB\", \"VTS\", \"OVE\"]",128"References FOO is not valid, must be in [\"ATT&CK\", \"CVE\", \"CWE\", \"BID\", \"MSB\", \"EDB\", \"US-CERT-VU\", \"ZDI\", \"URL\", \"WPVDB\", \"PACKETSTORM\", \"LOGO\", \"SOUNDTRACK\", \"OSVDB\", \"VTS\", \"OVE\"]",129"References NOCVE please include NOCVE values in the 'notes' section, rather than in 'references'",130"References AKA please include AKA values in the 'notes' section, rather than in 'references'",131"References ATTACK is not valid, must be in [\"ATT&CK\", \"CVE\", \"CWE\", \"BID\", \"MSB\", \"EDB\", \"US-CERT-VU\", \"ZDI\", \"URL\", \"WPVDB\", \"PACKETSTORM\", \"LOGO\", \"SOUNDTRACK\", \"OSVDB\", \"VTS\", \"OVE\"]"132]133end134end135136context 'when the license contains an invalid value' do137let(:mod_options) do138super().merge(license: 'MSF_LICENSE')139end140141it 'has errors' do142expect(subject.errors.full_messages).to eq ['License must include a valid license']143end144end145146context 'when the rank contains an invalid value' do147let(:mod_options) do148super().merge(rank: 'ExcellentRanking')149end150151it 'has errors' do152expect(subject.errors.full_messages).to eq ['Rank must include a valid module ranking']153end154end155156context 'when the author is missing' do157let(:mod_options) do158super().merge(author: [])159end160161it 'has errors' do162expect(subject.errors.full_messages).to eq ["Author can't be blank"]163end164end165166context 'when the author contains bad characters' do167let(:mod_options) do168super().merge(author: [169Msf::Author.new('@Foobar'),170Msf::Author.new('Foobar')171])172end173174it 'has errors' do175expect(subject.errors.full_messages).to eq ['Author must not include username handles, found "@Foobar". Try leaving it in a comment instead']176end177end178179context 'when the module name contains bad characters' do180let(:mod_options) do181super().merge(name: 'Testing <> bad & chars')182end183184it 'has errors' do185expect(subject.errors.full_messages).to eq ['Name must not contain the characters &<>']186end187end188189context 'when the name has non-printable ascii characters' do190let(:mod_options) do191super().merge(name: 'Testing human-readable printable ascii characters ≤')192end193194it 'has errors' do195expect(subject.errors.full_messages).to eq ['Name must only contain human-readable printable ascii characters']196end197end198199context 'when the module file path is not snake case' do200let(:mod_options) do201super().merge(file_path: 'modules/exploits/windows/smb/CVE_2020_0796_smbghost.rb')202end203204it 'has errors' do205expect(subject.errors.full_messages).to eq ['File path must be snake case, instead found "modules/exploits/windows/smb/CVE_2020_0796_smbghost.rb"']206end207end208209context 'when the description is missing' do210let(:mod_options) do211super().merge(description: nil)212end213214it 'has errors' do215expect(subject.errors.full_messages).to eq ["Description can't be blank"]216end217end218219context 'when the description has non-printable ascii characters' do220let(:mod_options) do221super().merge(description: "Testing human-readable printable ascii characters ≤\n\tand newlines/tabs")222end223224it 'has errors' do225expect(subject.errors.full_messages).to eq ['Description must only contain human-readable printable ascii characters, including newlines and tabs']226end227end228229context 'when the platform value is invalid', skip_before: true do230let(:mod_options) do231super().merge(platform: Msf::Module::PlatformList.new('foo'))232end233234it 'raises an ArgumentError' do235expect { subject }.to raise_error ArgumentError, 'No classes in Msf::Module::Platform for foo!'236end237end238239context 'when the arch array contains a valid value' do240it 'has no errors' do241expect(subject.errors.full_messages).to be_empty242end243end244245context 'when the arch array contains an invalid value' do246let(:mod_options) do247super().merge(arch: ["Rex::Arch::ARCH_X86"])248end249250it 'has errors' do251expect(subject.errors.full_messages).to eq ["Arch contains invalid values [\"Rex::Arch::ARCH_X86\"] - only [\"x86\", \"x86_64\", \"x64\", \"mips\", \"mipsle\", \"mipsbe\", \"mips64\", \"mips64le\", \"ppc\", \"ppce500v2\", \"ppc64\", \"ppc64le\", \"cbea\", \"cbea64\", \"sparc\", \"sparc64\", \"armle\", \"armbe\", \"aarch64\", \"cmd\", \"php\", \"tty\", \"java\", \"ruby\", \"dalvik\", \"python\", \"nodejs\", \"firefox\", \"zarch\", \"r\", \"riscv32be\", \"riscv32le\", \"riscv64be\", \"riscv64le\", \"loongarch64\"] is allowed"]252end253end254255context 'when the platform is missing and targets does not contain platform values' do256let(:mod_options) do257super().merge(platform: nil, targets: [Msf::Module::Target.new('Windows 10 v1903-1909 x64', { 'Arch' => ['x64'] })])258end259260it 'has errors' do261expect(subject.errors.full_messages).to eq ['Platform must be included either within targets or platform module metadata']262end263end264265context 'when the notes section contains sentinel values' do266let(:mod_options) do267new_module_options = {268notes: {269'Stability' => Msf::UNKNOWN_STABILITY,270'SideEffects' => Msf::UNKNOWN_SIDE_EFFECTS,271'Reliability' => Msf::UNKNOWN_RELIABILITY,272},273stability: Msf::UNKNOWN_STABILITY,274side_effects: Msf::UNKNOWN_SIDE_EFFECTS,275reliability: Msf::UNKNOWN_RELIABILITY,276}277super().merge(new_module_options)278end279280it 'has no errors' do281expect(subject.errors.full_messages).to be_empty282end283end284285context 'when the notes section contains in correct sentinel values' do286let(:mod_options) do287new_module_options = {288notes: {289'Stability' => [Msf::UNKNOWN_STABILITY],290'SideEffects' => [Msf::UNKNOWN_SIDE_EFFECTS],291'Reliability' => [Msf::UNKNOWN_RELIABILITY],292},293stability: [Msf::UNKNOWN_STABILITY],294side_effects: [Msf::UNKNOWN_SIDE_EFFECTS],295reliability: [Msf::UNKNOWN_RELIABILITY],296}297super().merge(new_module_options, rank: Msf::GreatRanking, rank_to_s: 'great')298end299300it 'has errors' do301expect(subject.errors.full_messages).to eq [302"Stability contains invalid values [[\"unknown-stability\"]] - only [\"crash-safe\", \"crash-service-restarts\", \"crash-service-down\", \"crash-os-restarts\", \"crash-os-down\", \"service-resource-loss\", \"os-resource-loss\"] is allowed",303"Side effects contains invalid values [[\"unknown-side-effects\"]] - only [\"artifacts-on-disk\", \"config-changes\", \"ioc-in-logs\", \"account-lockouts\", \"account-logout\", \"screen-effects\", \"audio-effects\", \"physical-effects\"] is allowed",304"Reliability contains invalid values [[\"unknown-reliability\"]] - only [\"first-attempt-fail\", \"repeatable-session\", \"unreliable-session\", \"event-dependent\"] is allowed"305]306end307end308309310context 'when the references contains ATT&CK values' do311let(:mod_options) do312super().merge(references: [313Msf::Module::SiteReference.new('ATT&CK', 'T1059.001'),314Msf::Module::SiteReference.new('ATT&CK', 'BAD1059.001')315])316end317318it 'has errors for invalid ATT&CK references' do319expect(subject.errors.full_messages).to eq ["References ATT&CK reference 'BAD1059.001' is invalid. Must start with one of [\"TA\", \"DS\", \"S\", \"M\", \"A\", \"G\", \"C\", \"T\"] and be followed by digits/periods, no whitespace."]320end321322context 'with only valid ATT&CK references' do323let(:mod_options) do324super().merge(references: [Msf::Module::SiteReference.new('ATT&CK', 'T1059.001')])325end326327it 'has no errors' do328expect(subject.errors.full_messages).to be_empty329end330end331end332end333end334335336