Loading .gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -9,3 +9,4 @@ .*.swp dupl.html unit-test-output coverage.out batch/batch.go +3 −2 Original line number Diff line number Diff line Loading @@ -131,7 +131,7 @@ the first user.`) Name: name, }) ask.CheckErr(err) key.Add(login, sshkey) ask.CheckErr(key.Add(login, sshkey)) userCreated := map[string]bool{ "SYSTEM": true, login: true, Loading Loading @@ -160,8 +160,9 @@ the first user.`) bulletin, err := os.Executable() ask.CheckErr(err) crontab := &strings.Builder{} template.Must(template.New("crontab").Parse(crontabTemplate)). err = template.Must(template.New("crontab").Parse(crontabTemplate)). Execute(crontab, map[string]string{"Bulletin": bulletin}) ask.CheckErr(err) fmt.Printf("Adding this to crontab:\n\n%s\n", crontab.String()) err = installCrontab(crontab.String()) ask.CheckErr(err) Loading batch/file.go +5 −6 Original line number Diff line number Diff line Loading @@ -8,12 +8,11 @@ import ( ) func touch(name string) error { f, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0666) f, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0600) // #nosec G304 -- path is from a known config location if err != nil { return err } f.Close() return nil return f.Close() } func installCrontab(crontab string) error { Loading @@ -23,8 +22,8 @@ func installCrontab(crontab string) error { return err } defer func() { f.Close() os.Remove(f.Name()) _ = f.Close() // #nosec G104 -- best-effort cleanup _ = os.Remove(f.Name()) // #nosec G104,G703 -- best-effort cleanup of temp file }() // Put the crontab in the temp file. Loading @@ -38,7 +37,7 @@ func installCrontab(crontab string) error { } // Have the crontab command read in the file. cmd := exec.Command("crontab", f.Name()) cmd := exec.Command("crontab", f.Name()) // #nosec G204,G702 -- argument is an os.CreateTemp path, not user input cmd.Stdin = strings.NewReader("") var stdout strings.Builder cmd.Stdout = &stdout Loading key/key.go +20 −11 Original line number Diff line number Diff line Loading @@ -43,15 +43,16 @@ func Add(login, public string) error { keyfile := path.Join(sshdir, "authorized_keys") // Open and lock the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return err } defer f.Close() if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { fd := int(f.Fd()) // #nosec G115 -- file descriptors always fit in int if err := unix.Flock(fd, unix.LOCK_EX); err != nil { return err } defer unix.Flock(int(f.Fd()), unix.LOCK_UN) // unlock after we're done defer unix.Flock(fd, unix.LOCK_UN) //nolint:errcheck // unlock after we're done // Check for duplicates. keycontent, err := io.ReadAll(f) Loading Loading @@ -95,7 +96,7 @@ func List(login string) ([]string, error) { keyfile := path.Join(sshdir, "authorized_keys") // Open the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return keys, err } Loading Loading @@ -157,15 +158,16 @@ func Delete(public string) error { keyfile := path.Join(sshdir, "authorized_keys") // Open the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return err } defer f.Close() if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { fd := int(f.Fd()) // #nosec G115 -- file descriptors always fit in int if err := unix.Flock(fd, unix.LOCK_EX); err != nil { return err } defer unix.Flock(int(f.Fd()), unix.LOCK_UN) // unlock after we're done defer unix.Flock(fd, unix.LOCK_UN) //nolint:errcheck // unlock after we're done // look for lines. doomed := doomedRaw.Marshal() Loading @@ -186,8 +188,12 @@ func Delete(public string) error { if _, err := f.Seek(0, io.SeekStart); err != nil { return fmt.Errorf("seek: %w", err) } f.Truncate(0) f.WriteString(strings.Join(keys, "\n") + "\n") if err := f.Truncate(0); err != nil { return fmt.Errorf("truncate: %w", err) } if _, err := f.WriteString(strings.Join(keys, "\n") + "\n"); err != nil { return fmt.Errorf("write: %w", err) } return nil } Loading @@ -204,15 +210,18 @@ func Fetch(login, nickname, username string) string { return fmt.Sprintln("ERROR: site nickname unknown.") } url := fmt.Sprintf(site, username) resp, err := http.Get(url) resp, err := http.Get(url) // #nosec G107 -- URL is constructed from a hardcoded allowlist of sites if err != nil { return fmt.Sprintf("ERROR: Failed to fetch ssh keys (%s).\n", err) } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) keys := 0 for scanner.Scan() { keyline := string(bytes.TrimSpace(scanner.Bytes())) Add(strings.ToUpper(login), keyline) if err := Add(strings.ToUpper(login), keyline); err != nil { return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err) } keys++ } switch keys { Loading pager/pager.go +3 −2 Original line number Diff line number Diff line Loading @@ -47,12 +47,13 @@ func Pager(content string) bool { pageSize := height - 1 // Reserve last line for prompt. // Set terminal raw mode. oldState, err := term.MakeRaw(int(os.Stdin.Fd())) fd := int(os.Stdin.Fd()) // #nosec G115 -- file descriptors always fit in int oldState, err := term.MakeRaw(fd) if err != nil { fmt.Printf("%s\n", content) return true } defer term.Restore(int(os.Stdin.Fd()), oldState) defer term.Restore(fd, oldState) // #nosec G115 start := 0 for { Loading Loading
.gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -9,3 +9,4 @@ .*.swp dupl.html unit-test-output coverage.out
batch/batch.go +3 −2 Original line number Diff line number Diff line Loading @@ -131,7 +131,7 @@ the first user.`) Name: name, }) ask.CheckErr(err) key.Add(login, sshkey) ask.CheckErr(key.Add(login, sshkey)) userCreated := map[string]bool{ "SYSTEM": true, login: true, Loading Loading @@ -160,8 +160,9 @@ the first user.`) bulletin, err := os.Executable() ask.CheckErr(err) crontab := &strings.Builder{} template.Must(template.New("crontab").Parse(crontabTemplate)). err = template.Must(template.New("crontab").Parse(crontabTemplate)). Execute(crontab, map[string]string{"Bulletin": bulletin}) ask.CheckErr(err) fmt.Printf("Adding this to crontab:\n\n%s\n", crontab.String()) err = installCrontab(crontab.String()) ask.CheckErr(err) Loading
batch/file.go +5 −6 Original line number Diff line number Diff line Loading @@ -8,12 +8,11 @@ import ( ) func touch(name string) error { f, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0666) f, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0600) // #nosec G304 -- path is from a known config location if err != nil { return err } f.Close() return nil return f.Close() } func installCrontab(crontab string) error { Loading @@ -23,8 +22,8 @@ func installCrontab(crontab string) error { return err } defer func() { f.Close() os.Remove(f.Name()) _ = f.Close() // #nosec G104 -- best-effort cleanup _ = os.Remove(f.Name()) // #nosec G104,G703 -- best-effort cleanup of temp file }() // Put the crontab in the temp file. Loading @@ -38,7 +37,7 @@ func installCrontab(crontab string) error { } // Have the crontab command read in the file. cmd := exec.Command("crontab", f.Name()) cmd := exec.Command("crontab", f.Name()) // #nosec G204,G702 -- argument is an os.CreateTemp path, not user input cmd.Stdin = strings.NewReader("") var stdout strings.Builder cmd.Stdout = &stdout Loading
key/key.go +20 −11 Original line number Diff line number Diff line Loading @@ -43,15 +43,16 @@ func Add(login, public string) error { keyfile := path.Join(sshdir, "authorized_keys") // Open and lock the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return err } defer f.Close() if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { fd := int(f.Fd()) // #nosec G115 -- file descriptors always fit in int if err := unix.Flock(fd, unix.LOCK_EX); err != nil { return err } defer unix.Flock(int(f.Fd()), unix.LOCK_UN) // unlock after we're done defer unix.Flock(fd, unix.LOCK_UN) //nolint:errcheck // unlock after we're done // Check for duplicates. keycontent, err := io.ReadAll(f) Loading Loading @@ -95,7 +96,7 @@ func List(login string) ([]string, error) { keyfile := path.Join(sshdir, "authorized_keys") // Open the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return keys, err } Loading Loading @@ -157,15 +158,16 @@ func Delete(public string) error { keyfile := path.Join(sshdir, "authorized_keys") // Open the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return err } defer f.Close() if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil { fd := int(f.Fd()) // #nosec G115 -- file descriptors always fit in int if err := unix.Flock(fd, unix.LOCK_EX); err != nil { return err } defer unix.Flock(int(f.Fd()), unix.LOCK_UN) // unlock after we're done defer unix.Flock(fd, unix.LOCK_UN) //nolint:errcheck // unlock after we're done // look for lines. doomed := doomedRaw.Marshal() Loading @@ -186,8 +188,12 @@ func Delete(public string) error { if _, err := f.Seek(0, io.SeekStart); err != nil { return fmt.Errorf("seek: %w", err) } f.Truncate(0) f.WriteString(strings.Join(keys, "\n") + "\n") if err := f.Truncate(0); err != nil { return fmt.Errorf("truncate: %w", err) } if _, err := f.WriteString(strings.Join(keys, "\n") + "\n"); err != nil { return fmt.Errorf("write: %w", err) } return nil } Loading @@ -204,15 +210,18 @@ func Fetch(login, nickname, username string) string { return fmt.Sprintln("ERROR: site nickname unknown.") } url := fmt.Sprintf(site, username) resp, err := http.Get(url) resp, err := http.Get(url) // #nosec G107 -- URL is constructed from a hardcoded allowlist of sites if err != nil { return fmt.Sprintf("ERROR: Failed to fetch ssh keys (%s).\n", err) } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) keys := 0 for scanner.Scan() { keyline := string(bytes.TrimSpace(scanner.Bytes())) Add(strings.ToUpper(login), keyline) if err := Add(strings.ToUpper(login), keyline); err != nil { return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err) } keys++ } switch keys { Loading
pager/pager.go +3 −2 Original line number Diff line number Diff line Loading @@ -47,12 +47,13 @@ func Pager(content string) bool { pageSize := height - 1 // Reserve last line for prompt. // Set terminal raw mode. oldState, err := term.MakeRaw(int(os.Stdin.Fd())) fd := int(os.Stdin.Fd()) // #nosec G115 -- file descriptors always fit in int oldState, err := term.MakeRaw(fd) if err != nil { fmt.Printf("%s\n", content) return true } defer term.Restore(int(os.Stdin.Fd()), oldState) defer term.Restore(fd, oldState) // #nosec G115 start := 0 for { Loading