Skip to content

Commit 4514ae4

Browse files
committed
Added valid-shell command.
1 parent b226592 commit 4514ae4

File tree

4 files changed

+172
-0
lines changed

4 files changed

+172
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. The format
1111
- remove-shell command.
1212
- reset command.
1313
- sync command.
14+
- valid-shell command.
1415
### Changed
1516
- Project
1617
- the classification of directories under internal/applets.

docs/introduction/en/CommandAppletList.md

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
| unexpand| Convert N space to TAB (default:N=8)|
5656
| unix2dos| Change LF to CRLF|
5757
| uuidgeb| Print UUID (Universal Unique IDentifier|
58+
| valid-shell| Verify if /etc/shells is valid|
5859
| wc | Word Counter|
5960
| wget | The non-interactive network downloader|
6061
| which | Returns the file path which would be executed in the current environment|

internal/applets/applet.go

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"github.com/nao1215/mimixbox/internal/applets/mbutils/path"
4848
"github.com/nao1215/mimixbox/internal/applets/mbutils/sddf"
4949
"github.com/nao1215/mimixbox/internal/applets/mbutils/serial"
50+
validShell "github.com/nao1215/mimixbox/internal/applets/mbutils/valid-shell"
5051
"github.com/nao1215/mimixbox/internal/applets/shellutils/base64"
5152
"github.com/nao1215/mimixbox/internal/applets/shellutils/basename"
5253
"github.com/nao1215/mimixbox/internal/applets/shellutils/chroot"
@@ -148,6 +149,7 @@ func init() {
148149
"unexpand": {unexpand.Run, "Convert N space to TAB(default:N=8)"},
149150
"unix2dos": {unix2dos.Run, "Change LF to CRLF"},
150151
"uuidgen": {uuidgen.Run, "Print UUID (Universal Unique IDentifier"},
152+
"valid-shell": {validShell.Run, "Verify if /etc/shells is valid"},
151153
"wc": {wc.Run, "Word Count"},
152154
"wget": {wget.Run, "The non-interactive network downloader"},
153155
"which": {which.Run, "Returns the file path which would be executed in the current environment"},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//
2+
// mimixbox/internal/applets/debianutils/valid-shell/valid-shell.go
3+
//
4+
// Copyright 2021 Naohiro CHIKAMATSU
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
package validShell
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"os/exec"
23+
"strings"
24+
25+
"github.com/jessevdk/go-flags"
26+
mb "github.com/nao1215/mimixbox/internal/lib"
27+
)
28+
29+
const cmdName string = "valid-shell"
30+
31+
const version = "1.0.0"
32+
33+
var osExit = os.Exit
34+
35+
type options struct {
36+
Show bool `short:"s" long:"show" description:"Print contents of /etc/shells"`
37+
Fix bool `short:"f" long:"fix" description:"Fix problems in /etc/shells"`
38+
Version bool `short:"v" long:"version" description:"Show valid-shell command version"`
39+
}
40+
41+
// Exit code
42+
const (
43+
ExitSuccess int = iota // 0
44+
ExitFailuer
45+
)
46+
47+
func Run() (int, error) {
48+
var opts options
49+
var err error
50+
51+
if _, err = parseArgs(&opts); err != nil {
52+
return ExitFailuer, nil
53+
}
54+
55+
return validShell(opts)
56+
}
57+
58+
func validShell(opts options) (int, error) {
59+
if opts.Show {
60+
return printShellsFile()
61+
} else if opts.Fix {
62+
return fix()
63+
}
64+
return valid()
65+
}
66+
67+
func printShellsFile() (int, error) {
68+
lines, err := mb.ReadFileToStrList(mb.ShellsFilePath)
69+
if err != nil {
70+
return ExitFailuer, err
71+
}
72+
for _, v := range lines {
73+
fmt.Fprintf(os.Stdout, "%s", v)
74+
}
75+
return ExitSuccess, nil
76+
}
77+
78+
func fix() (int, error) {
79+
f, err := os.OpenFile(mb.TmpShellsFile(), os.O_CREATE|os.O_WRONLY, 0644)
80+
if err != nil {
81+
return ExitFailuer, err
82+
}
83+
defer f.Close()
84+
85+
lines, err := mb.ReadFileToStrList(mb.ShellsFilePath)
86+
if err != nil {
87+
return ExitFailuer, err
88+
}
89+
90+
lines = mb.ChopAll(lines)
91+
for _, v := range lines {
92+
if strings.HasPrefix(v, "#") {
93+
fmt.Fprintln(f, v)
94+
continue
95+
}
96+
if isFalseCmd(v) {
97+
continue // NG: Bupass
98+
}
99+
if mb.Exists(v) {
100+
fmt.Fprintln(f, v)
101+
} // else is NG: Bypass
102+
}
103+
104+
err = mb.Copy(mb.TmpShellsFile(), mb.ShellsFilePath)
105+
if err != nil {
106+
mb.RemoveFile(mb.TmpShellsFile(), false)
107+
return ExitFailuer, err
108+
}
109+
mb.RemoveFile(mb.TmpShellsFile(), false)
110+
111+
return ExitSuccess, nil
112+
}
113+
114+
func valid() (int, error) {
115+
lines, err := mb.ReadFileToStrList(mb.ShellsFilePath)
116+
if err != nil {
117+
return ExitFailuer, err
118+
}
119+
120+
lines = mb.ChopAll(lines)
121+
for _, v := range lines {
122+
if strings.HasPrefix(v, "#") {
123+
continue
124+
}
125+
if isFalseCmd(v) {
126+
fmt.Fprintf(os.Stdout, "NG: %s (not preferable for security)\n", v)
127+
continue
128+
}
129+
if mb.Exists(v) {
130+
fmt.Fprintf(os.Stdout, "OK: %s\n", v)
131+
} else {
132+
fmt.Fprintf(os.Stdout, "NG: %s (not exist in the system)\n", v)
133+
}
134+
}
135+
return ExitSuccess, nil
136+
}
137+
138+
func isFalseCmd(str string) bool {
139+
path, err := exec.LookPath("false")
140+
if err != nil {
141+
return false
142+
}
143+
return path == str
144+
}
145+
146+
func parseArgs(opts *options) ([]string, error) {
147+
p := initParser(opts)
148+
149+
args, err := p.Parse()
150+
if err != nil {
151+
return nil, err
152+
}
153+
154+
if opts.Version {
155+
mb.ShowVersion(cmdName, version)
156+
osExit(ExitSuccess)
157+
}
158+
159+
return args, nil
160+
}
161+
162+
func initParser(opts *options) *flags.Parser {
163+
parser := flags.NewParser(opts, flags.Default)
164+
parser.Name = cmdName
165+
parser.Usage = "[OPTIONS]"
166+
167+
return parser
168+
}

0 commit comments

Comments
 (0)