// Copyright (C) 2021 Simon Ruderich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
package config
import (
"fmt"
"io/fs"
"os"
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"ruderich.org/simon/safcm"
)
func TestLoadPermissions(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
defer os.Chdir(cwd)
err = os.Chdir("../testdata/project")
if err != nil {
t.Fatal(err)
}
tests := []struct {
group string
exp map[string]*safcm.File
expErr error
}{
{
"empty",
nil,
nil,
},
{
"group",
map[string]*safcm.File{
"/": {
Path: "/",
Mode: fs.ModeDir | 0755 | fs.ModeSetgid,
Uid: -1,
Gid: -1,
},
"/etc": {
Path: "/etc",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc/.hidden": {
Path: "/etc/.hidden",
Mode: 0100 | fs.ModeSetuid | fs.ModeSetgid | fs.ModeSticky,
Uid: -1,
Gid: -1,
Data: []byte("..."),
},
"/etc/motd": {
Path: "/etc/motd",
Mode: 0644,
Uid: -1,
Gid: -1,
Data: []byte(`Welcome to
{{- if .IsHost "host1.example.org"}} Host ONE
{{- else if "host2"}} Host TWO
{{- end}}
{{if .InGroup "detected_linux"}}
This is GNU/Linux host
{{end}}
{{if .InGroup "detected_freebsd"}}
This is FreeBSD host
{{end}}
`),
},
"/etc/rc.local": {
Path: "/etc/rc.local",
Mode: 0700,
Uid: -1,
Gid: -1,
Data: []byte("#!/bin/sh\n"),
},
"/etc/resolv.conf": {
Path: "/etc/resolv.conf",
Mode: 0641,
User: "user",
Uid: -1,
Group: "group",
Gid: -1,
Data: []byte("nameserver ::1\n"),
},
"/etc/test": {
Path: "/etc/test",
Mode: fs.ModeSymlink | 0777,
Uid: -1,
Gid: -1,
Data: []byte("doesnt-exist"),
},
},
nil,
},
{
"permissions-invalid-execute",
map[string]*safcm.File{
"/": {
Path: "/",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc": {
Path: "/etc",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc/rc.local": {
Path: "/etc/rc.local",
Mode: 0755,
Uid: -1,
Gid: -1,
Data: []byte("#!/bin/sh\n"),
},
},
fmt.Errorf("permissions-invalid-execute/permissions.yaml: \"/etc/rc.local\": trying to remove +x from file, manually chmod -x in files/"),
},
{
"permissions-invalid-line",
map[string]*safcm.File{
"/": {
Path: "/",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc": {
Path: "/etc",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc/resolv.conf": {
Path: "/etc/resolv.conf",
Mode: 0644,
Uid: -1,
Gid: -1,
Data: []byte("nameserver ::1\n"),
},
},
fmt.Errorf("permissions-invalid-line/permissions.yaml: invalid line \"invalid line\" (expected [ ])"),
},
{
"permissions-invalid-path",
nil,
fmt.Errorf("permissions-invalid-path/permissions.yaml: \"/does/not/exist\" does not exist in files/"),
},
{
"permissions-invalid-permission",
map[string]*safcm.File{
"/": {
Path: "/",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc": {
Path: "/etc",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc/resolv.conf": {
Path: "/etc/resolv.conf",
Mode: 0644,
Uid: -1,
Gid: -1,
Data: []byte("nameserver ::1\n"),
},
},
fmt.Errorf("permissions-invalid-permission/permissions.yaml: invalid permission \"u=rwg=r\" (expected e.g. \"0644\" or \"01777\")"),
},
{
"permissions-invalid-permission-int",
map[string]*safcm.File{
"/": {
Path: "/",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc": {
Path: "/etc",
Mode: fs.ModeDir | 0755,
Uid: -1,
Gid: -1,
},
"/etc/resolv.conf": {
Path: "/etc/resolv.conf",
Mode: 0644,
Uid: -1,
Gid: -1,
Data: []byte("nameserver ::1\n"),
},
},
fmt.Errorf("permissions-invalid-permission-int/permissions.yaml: invalid permission 066066 (expected e.g. 0644 or 01777)"),
},
}
for _, tc := range tests {
// Use LoadFiles() so we work on real data and don't make any
// mistakes generating it
files, err := LoadFiles(tc.group)
if err != nil {
t.Fatalf("%s: err = %#v, want nil",
tc.group, err)
}
err = LoadPermissions(tc.group, files)
if !reflect.DeepEqual(tc.exp, files) {
t.Errorf("%s: res: %s", tc.group,
cmp.Diff(tc.exp, files))
}
// Ugly but the simplest way to compare errors (including nil)
if fmt.Sprintf("%s", err) != fmt.Sprintf("%s", tc.expErr) {
t.Errorf("%s: err = %#v, want %#v",
tc.group, err, tc.expErr)
}
}
}