github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/file/cataloger/executable/elf_test.go (about)

     1  package executable
     2  
     3  import (
     4  	"debug/elf"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/anchore/syft/syft/file"
    14  	"github.com/anchore/syft/syft/internal/unionreader"
    15  )
    16  
    17  func Test_findELFSecurityFeatures(t *testing.T) {
    18  
    19  	readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
    20  		t.Helper()
    21  		f, err := os.Open(filepath.Join("test-fixtures/elf", fixture))
    22  		require.NoError(t, err)
    23  		return f
    24  	}
    25  
    26  	tests := []struct {
    27  		name         string
    28  		fixture      string
    29  		want         *file.ELFSecurityFeatures
    30  		wantStripped bool
    31  	}{
    32  		{
    33  			name:    "detect canary",
    34  			fixture: "bin/with_canary",
    35  			want: &file.ELFSecurityFeatures{
    36  				StackCanary:              boolRef(true), // ! important !
    37  				RelocationReadOnly:       file.RelocationReadOnlyNone,
    38  				LlvmSafeStack:            boolRef(false),
    39  				LlvmControlFlowIntegrity: boolRef(false),
    40  				ClangFortifySource:       boolRef(false),
    41  			},
    42  		},
    43  		{
    44  			name:    "detect nx",
    45  			fixture: "bin/with_nx",
    46  			want: &file.ELFSecurityFeatures{
    47  				StackCanary:              boolRef(false),
    48  				NoExecutable:             true, // ! important !
    49  				RelocationReadOnly:       file.RelocationReadOnlyNone,
    50  				LlvmSafeStack:            boolRef(false),
    51  				LlvmControlFlowIntegrity: boolRef(false),
    52  				ClangFortifySource:       boolRef(false),
    53  			},
    54  		},
    55  		{
    56  			name:    "detect relro",
    57  			fixture: "bin/with_relro",
    58  			want: &file.ELFSecurityFeatures{
    59  				StackCanary:              boolRef(false),
    60  				RelocationReadOnly:       file.RelocationReadOnlyFull, // ! important !
    61  				LlvmSafeStack:            boolRef(false),
    62  				LlvmControlFlowIntegrity: boolRef(false),
    63  				ClangFortifySource:       boolRef(false),
    64  			},
    65  		},
    66  		{
    67  			name:    "detect partial relro",
    68  			fixture: "bin/with_partial_relro",
    69  			want: &file.ELFSecurityFeatures{
    70  				StackCanary:              boolRef(false),
    71  				RelocationReadOnly:       file.RelocationReadOnlyPartial, // ! important !
    72  				LlvmSafeStack:            boolRef(false),
    73  				LlvmControlFlowIntegrity: boolRef(false),
    74  				ClangFortifySource:       boolRef(false),
    75  			},
    76  		},
    77  		{
    78  			name:    "detect pie",
    79  			fixture: "bin/with_pie",
    80  			want: &file.ELFSecurityFeatures{
    81  				StackCanary:                   boolRef(false),
    82  				RelocationReadOnly:            file.RelocationReadOnlyNone,
    83  				PositionIndependentExecutable: true, // ! important !
    84  				DynamicSharedObject:           true, // ! important !
    85  				LlvmSafeStack:                 boolRef(false),
    86  				LlvmControlFlowIntegrity:      boolRef(false),
    87  				ClangFortifySource:            boolRef(false),
    88  			},
    89  		},
    90  		{
    91  			name:    "detect dso",
    92  			fixture: "bin/pie_false_positive.so",
    93  			want: &file.ELFSecurityFeatures{
    94  				StackCanary:                   boolRef(false),
    95  				RelocationReadOnly:            file.RelocationReadOnlyPartial,
    96  				NoExecutable:                  true,
    97  				PositionIndependentExecutable: false, // ! important !
    98  				DynamicSharedObject:           true,  // ! important !
    99  				LlvmSafeStack:                 boolRef(false),
   100  				LlvmControlFlowIntegrity:      boolRef(false),
   101  				ClangFortifySource:            boolRef(false),
   102  			},
   103  		},
   104  		{
   105  			name:    "detect safestack",
   106  			fixture: "bin/with_safestack",
   107  			want: &file.ELFSecurityFeatures{
   108  				NoExecutable:                  true,
   109  				StackCanary:                   boolRef(false),
   110  				RelocationReadOnly:            file.RelocationReadOnlyPartial,
   111  				PositionIndependentExecutable: false,
   112  				DynamicSharedObject:           false,
   113  				LlvmSafeStack:                 boolRef(true), // ! important !
   114  				LlvmControlFlowIntegrity:      boolRef(false),
   115  				ClangFortifySource:            boolRef(false),
   116  			},
   117  		},
   118  		{
   119  			name:    "detect cfi",
   120  			fixture: "bin/with_cfi",
   121  			want: &file.ELFSecurityFeatures{
   122  				NoExecutable:                  true,
   123  				StackCanary:                   boolRef(false),
   124  				RelocationReadOnly:            file.RelocationReadOnlyPartial,
   125  				PositionIndependentExecutable: false,
   126  				DynamicSharedObject:           false,
   127  				LlvmSafeStack:                 boolRef(false),
   128  				LlvmControlFlowIntegrity:      boolRef(true), // ! important !
   129  				ClangFortifySource:            boolRef(false),
   130  			},
   131  		},
   132  		{
   133  			name:    "detect fortify",
   134  			fixture: "bin/with_fortify",
   135  			want: &file.ELFSecurityFeatures{
   136  				NoExecutable:                  true,
   137  				StackCanary:                   boolRef(false),
   138  				RelocationReadOnly:            file.RelocationReadOnlyPartial,
   139  				PositionIndependentExecutable: false,
   140  				DynamicSharedObject:           false,
   141  				LlvmSafeStack:                 boolRef(false),
   142  				LlvmControlFlowIntegrity:      boolRef(false),
   143  				ClangFortifySource:            boolRef(true), // ! important !
   144  			},
   145  		},
   146  	}
   147  	for _, tt := range tests {
   148  		t.Run(tt.name, func(t *testing.T) {
   149  			f, err := elf.NewFile(readerForFixture(t, tt.fixture))
   150  			require.NoError(t, err)
   151  
   152  			got := findELFSecurityFeatures(f)
   153  
   154  			if d := cmp.Diff(tt.want, got); d != "" {
   155  				t.Errorf("findELFSecurityFeatures() mismatch (-want +got):\n%s", d)
   156  			}
   157  		})
   158  	}
   159  }
   160  
   161  func Test_elfHasEntrypoint(t *testing.T) {
   162  
   163  	readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
   164  		t.Helper()
   165  		f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
   166  		require.NoError(t, err)
   167  		return f
   168  	}
   169  
   170  	tests := []struct {
   171  		name    string
   172  		fixture string
   173  		want    bool
   174  	}{
   175  		{
   176  			name:    "shared lib",
   177  			fixture: "bin/libhello.so",
   178  			want:    false,
   179  		},
   180  		{
   181  			name:    "application",
   182  			fixture: "bin/hello_linux",
   183  			want:    true,
   184  		},
   185  	}
   186  	for _, tt := range tests {
   187  		t.Run(tt.name, func(t *testing.T) {
   188  			f, err := elf.NewFile(readerForFixture(t, tt.fixture))
   189  			require.NoError(t, err)
   190  			assert.Equal(t, tt.want, elfHasEntrypoint(f))
   191  		})
   192  	}
   193  }
   194  
   195  func Test_elfHasExports(t *testing.T) {
   196  	readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
   197  		t.Helper()
   198  		f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
   199  		require.NoError(t, err)
   200  		return f
   201  	}
   202  
   203  	tests := []struct {
   204  		name    string
   205  		fixture string
   206  		want    bool
   207  	}{
   208  		{
   209  			name:    "shared lib",
   210  			fixture: "bin/libhello.so",
   211  			want:    true,
   212  		},
   213  		{
   214  			name:    "application",
   215  			fixture: "bin/hello_linux",
   216  			want:    false,
   217  		},
   218  	}
   219  	for _, tt := range tests {
   220  		t.Run(tt.name, func(t *testing.T) {
   221  			f, err := elf.NewFile(readerForFixture(t, tt.fixture))
   222  			require.NoError(t, err)
   223  			assert.Equal(t, tt.want, elfHasExports(f))
   224  		})
   225  	}
   226  }