Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced Git Resource Example #301

Merged
merged 20 commits into from
Dec 23, 2024
Merged
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 86 additions & 55 deletions src/courses/advanced/05.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,49 @@ tar xzvf resources/git_test_target.tar.gz

This will generate a `git_test` repository which we will use for these examples.
Amndeep7 marked this conversation as resolved.
Show resolved Hide resolved

Now let's write some tests and confirm that they run. You can put these tests in the `example.rb` file generated in the `controls` folder of your `my_git` InSpec profile. These tests are written using the `command` resource which is provided by InSpec. We will write a `git` resource in this section to improve this test. **Note that you will need to put the full directory path of the `.git` file from your `git_test_target` repository as the `git_dir` value on line 4 of `example.rb`. To get the full path of your current location in the terminal, use `pwd`.**
Now let's write some tests and confirm that they run. You can put these tests in the `example.rb` file generated in the `controls` folder of your `my_git` InSpec profile. These tests are written using the `command` resource which is provided by InSpec. We will write a `git` resource in this section to improve this test.

```ruby
# encoding: utf-8
# copyright: 2018, The Authors
::: note Input review
You will need to put the full directory path of the `.git` file from your `git_test_target` repository as the value for the `git_dir` input in your `inspec.yml`. To get the full path of your current location in the terminal, use `pwd`. The value in the codeblock below should be correct, but it's always good to doublecheck.
:::
Amndeep7 marked this conversation as resolved.
Show resolved Hide resolved

git_dir = "/workspaces/saf-training-lab-environment/git_test_target_target/.git"
Amndeep7 marked this conversation as resolved.
Show resolved Hide resolved
::: code-tabs

@tab inspec.yml
```yaml
name: my_git
title: InSpec Profile
maintainer: The Authors
copyright: The Authors
copyright_email: [email protected]
license: Apache-2.0
summary: An InSpec Compliance Profile
version: 0.1.0
supports:
platform: os

inputs:
- name: git_dir
type: String
value: /workspaces/saf-training-lab-environment/git_test_target/.git
```

@tab example.rb
```ruby
git_dir = input('git_dir')

# The following branches should exist
describe command("git --git-dir #{git_dir} branch") do
its('stdout') { should match /main/ }
describe command("git --git-dir #{git_dir} branch --format='%(refname:short)'").stdout.lines.map(&:strip) do
it { should include 'main' }
end

describe command("git --git-dir #{git_dir} branch") do
its('stdout') { should match /testBranch/ }
describe command("git --git-dir #{git_dir} branch --format='%(refname:short)'").stdout.lines.map(&:strip) do
it { should include 'testBranch' }
end

# What is the current branch
describe command("git --git-dir #{git_dir} branch") do
its('stdout') { should match /^\* main/ }
describe command("git --git-dir #{git_dir} branch --show-current").stdout.strip do
it { should match 'main' }
Amndeep7 marked this conversation as resolved.
Show resolved Hide resolved
end

# What is the latest commit
Expand All @@ -76,6 +99,7 @@ describe command("git --git-dir #{git_dir} log --skip=1 -1 --pretty=format:'%h'"
its('stdout') { should match /edc207f/ }
end
```
:::

::: code-tabs

Expand All @@ -96,12 +120,12 @@ Version: 0.1.0
Target: local://
Target ID: 6dcb9e6f-5ede-5474-9521-595fadf5c7ce

Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /main/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /testBranch/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /^\* main/
["main", "testBranch"]
✔ is expected to include "main"
["main", "testBranch"]
✔ is expected to include "testBranch"
main
✔ is expected to match "main"
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log -1 --pretty=format:'%h'`
✔ stdout is expected to match /7a748c6/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log --skip=1 -1 --pretty=format:'%h'`
Expand Down Expand Up @@ -149,7 +173,7 @@ inspec exec my_git
```bash
Redirecting to cinc-auditor...

[2023-02-22T03:21:41+00:00] ERROR: Failed to load profile git: Failed to load source for controls/example.rb: undefined method `git' for #<Inspec::ControlEvalContext:0x000000000540af38>
[2023-02-22T03:21:41+00:00] ERROR: Failed to load profile my_git: Failed to load source for controls/example.rb: undefined method `git' for #<Inspec::ControlEvalContext:0x000000000540af38>

Profile: InSpec Profile (my_git)
Version: 0.1.0
Expand All @@ -171,9 +195,6 @@ We should get an error because the git method and resource are not defined yet.
Let's start by creating a new file called `git.rb` in the `libraries` directory. If you do not already have a `libraries` directory, you can make one in the `my_git` InSpec profile directory. The content of the file should look like this:

```ruby
# encoding: utf-8
# copyright: 2019, The Authors

class Git < Inspec.resource(1)
name 'git'
end
Expand All @@ -197,7 +218,7 @@ inspec exec my_git
@tab Output

```bash
[2023-02-22T03:25:57+00:00] ERROR: Failed to load profile git: Failed to load source for controls/example.rb: wrong number of arguments (given 1, expected 0)
[2023-02-22T03:25:57+00:00] ERROR: Failed to load profile my_git: Failed to load source for controls/example.rb: wrong number of arguments (given 1, expected 0)

Profile: InSpec Profile (my_git)
Version: 0.1.0
Expand All @@ -219,9 +240,6 @@ Each resource will require an initialization method.
For our git.rb file let's add that initialization method:

```ruby
# encoding: utf-8
# copyright: 2019, The Authors

class Git < Inspec.resource(1)
name 'git'

Expand Down Expand Up @@ -255,14 +273,14 @@ Target ID: 6dcb9e6f-5ede-5474-9521-595fadf5c7ce
git
× branches
undefined method `branches' for #<#<Class:0x00000000041485a8>:0x00000000043620c8>
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /testBranch/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /^\* main/
["main", "testBranch"]
✔ is expected to include "testBranch"
main
✔ is expected to match "main"
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log -1 --pretty=format:'%h'`
✔ stdout is expected to match /edc207f/
✔ stdout is expected to match /7a748c6/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log --skip=1 -1 --pretty=format:'%h'`
✔ stdout is expected to match /8c30bff/
✔ stdout is expected to match /edc207f/

Test Summary: 4 successful, 1 failure, 0 skipped
```
Expand All @@ -274,9 +292,6 @@ The test will run but we will get an error saying we do not have a `branches` me
Let's go back to our git.rb file to fix that by adding a `branches` method:

```ruby
# encoding: utf-8
# copyright: 2019, The Authors

class Git < Inspec.resource(1)
name 'git'

Expand Down Expand Up @@ -312,10 +327,10 @@ Target ID: 6dcb9e6f-5ede-5474-9521-595fadf5c7ce
git
× branches is expected to include "main"
expected nil to include "main", but it does not respond to `include?`
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /testBranch/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git branch`
stdout is expected to match /^\* main/
["main", "testBranch"]
✔ is expected to include "testBranch"
main
✔ is expected to match "main"
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log -1 --pretty=format:'%h'`
✔ stdout is expected to match /7a748c6/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log --skip=1 -1 --pretty=format:'%h'`
Expand All @@ -331,9 +346,6 @@ Now the error message says that the `branches` method is returning a null value
To resolve this problem, we can use the `inspec` helper method to invoke the built-in `command` resource to extract this data as shown below:

```ruby
# encoding: utf-8
# copyright: 2019, The Authors

class Git < Inspec.resource(1)
name 'git'

Expand All @@ -342,7 +354,7 @@ class Git < Inspec.resource(1)
end

def branches
inspec.command("git --git-dir #{@path} branch").stdout
inspec.command("git --git-dir #{@path} branch --format='%(refname:short)'").stdout.lines.map(&:strip)
end

end
Expand Down Expand Up @@ -385,9 +397,9 @@ Target ID: 6dcb9e6f-5ede-5474-9521-595fadf5c7ce
× current_branch
undefined method `current_branch' for #<#<Class:0x0000000005400588>:0x00000000053fd0b8>
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log -1 --pretty=format:'%h'`
✔ stdout is expected to match /8c30bff/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log --skip=1 -1 --pretty=format:'%h'`
✔ stdout is expected to match /7a748c6/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log --skip=1 -1 --pretty=format:'%h'`
✔ stdout is expected to match /edc207f/

Test Summary: 6 successful, 1 failure, 0 skipped
```
Expand All @@ -397,9 +409,6 @@ Test Summary: 6 successful, 1 failure, 0 skipped
Let's head over to the git.rb file to create the `current_branch` method we are invoking in the above test:

```ruby
# encoding: utf-8
# copyright: 2019, The Authors

class Git < Inspec.resource(1)
name 'git'

Expand All @@ -408,19 +417,41 @@ class Git < Inspec.resource(1)
end

def branches
inspec.command("git --git-dir #{@path} branch").stdout
inspec.command("git --git-dir #{@path} branch --format='%(refname:short)'").stdout.lines.map(&:strip)
end

def current_branch
Amndeep7 marked this conversation as resolved.
Show resolved Hide resolved
branch_name = inspec.command("git --git-dir #{@path} branch").stdout.strip.split("\n").find do |name|
name.start_with?('*')
end
branch_name.gsub(/^\*/,'').strip
inspec.command("git --git-dir #{@path} branch --show-current").stdout.strip
end

end
```

::: tip 1337 h4x0rs
Amndeep7 marked this conversation as resolved.
Show resolved Hide resolved
While InSpec controls are intended on being easy to read by both software/cybersecurity engineers AND assessors, InSpec resources are an opportunity to flex those programming muscles and use that arcane sysadmin knowledge to do things in the most optimal way.

Here's an alternative implementation for `current_branch`:
```ruby
def current_branch
branch_name = inspec.command("git --git-dir #{@path} branch").stdout.gsub(" ", "").split("\n").find do |name|
name.start_with?('*')
end
branch_name[1..-1]
end
```

Note how this iterates over the baseline `git branch` output which looks like this:
```bash
$ git branch
* main
testBranch
```

And then we do some fancy stuff with string manipulation while searching for the magic string that starts with an asterisk (which marks the current branch). We're able to leverage the full power of Ruby here!

However, if you're a command line expert (or read the docs!), you'd know that you could just pass a single additional flag to the `git branch` function that spits out the exact answer that we want - don't get carried away when a simple solution exists!
:::

Now we can run the profile again.

::: code-tabs
Expand All @@ -446,7 +477,7 @@ Target ID: 6dcb9e6f-5ede-5474-9521-595fadf5c7ce
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log -1 --pretty=format:'%h'`
✔ stdout is expected to match /7a748c6/
Command: `git --git-dir /workspaces/saf-training-lab-environment/git_test_target/.git log --skip=1 -1 --pretty=format:'%h'`
✔ stdout is expected to match /8c30bff/
✔ stdout is expected to match /edc207f/

Test Summary: 7 successful, 0 failures, 0 skipped
```
Expand All @@ -473,7 +504,7 @@ Invoking the InSpec shell with `inspec shell` will give you access to all the co

@tab Command
```sh
inspec shell --depends git
inspec shell --depends my_git
```
@tab Output
```sh
Expand Down Expand Up @@ -506,4 +537,4 @@ inspec shell --depends my_git/libraries

If you edit the resource class file, you'll need to exit the shell and re-launch it for the updates to be available.

From here, we can examine our custom resource in a sandbox in the same way that we do with core resources.
From here, we can examine our custom resource in a sandbox in the same way that we do with core resources.
Loading