44
55set -e
66
7+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8+ # INPUT VALIDATION (SECURITY: OWASP A03 - Injection Prevention)
9+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
10+
711PLUGIN_DIR=" ${1:- .} "
12+
13+ # Security: Prevent directory traversal
14+ if [[ " $PLUGIN_DIR " == * " .." * ]]; then
15+ echo " ❌ Security Error: Directory traversal not allowed"
16+ exit 1
17+ fi
18+
19+ # Security: Whitelist allowed path characters
20+ if [[ ! " $PLUGIN_DIR " =~ ^[a-zA-Z0-9/_.-]+$ ]]; then
21+ echo " ❌ Security Error: Invalid characters in path"
22+ exit 1
23+ fi
24+
25+ # Validate directory exists
26+ if [ ! -d " $PLUGIN_DIR " ]; then
27+ echo " ❌ Error: Directory does not exist: $PLUGIN_DIR "
28+ exit 1
29+ fi
30+
831ERRORS=0
932WARNINGS=0
1033
@@ -16,105 +39,105 @@ echo ""
1639if [ -d " $PLUGIN_DIR /skills" ]; then
1740 echo " Validating skill.md frontmatter..."
1841 echo " "
19- for skill_dir in " $PLUGIN_DIR /skills" /* / ; do
20- if [ -d " $skill_dir " ]; then
21- SKILL_NAME=$( basename " $skill_dir " )
22- SKILL_MD=" $skill_dir /skill.md"
42+ for skill_dir in " $PLUGIN_DIR /skills" /* /; do
43+ if [ -d " $skill_dir " ]; then
44+ SKILL_NAME=$( basename " $skill_dir " )
45+ SKILL_MD=" $skill_dir /skill.md"
2346
24- echo " Checking skill: $SKILL_NAME "
47+ echo " Checking skill: $SKILL_NAME "
2548
26- if [ ! -f " $SKILL_MD " ]; then
27- echo " ❌ skill.md not found"
28- (( ERRORS ++ ))
29- continue
30- fi
49+ if [ ! -f " $SKILL_MD " ]; then
50+ echo " ❌ skill.md not found"
51+ (( RRORS ++ )) || true
52+ continue
53+ fi
3154
32- # Check for frontmatter block
33- if ! grep -q " ^---$" " $SKILL_MD " ; then
34- echo " ❌ No frontmatter found (missing --- markers)"
35- (( ERRORS ++ ))
36- continue
37- fi
55+ # Check for frontmatter block
56+ if ! grep -q " ^---$" " $SKILL_MD " ; then
57+ echo " ❌ No frontmatter found (missing --- markers)"
58+ (( RRORS ++ )) || true
59+ continue
60+ fi
3861
39- # Extract frontmatter (between first two --- markers)
40- FRONTMATTER=$( sed -n ' /^---$/,/^---$/p' " $SKILL_MD " | sed ' 1d;$d' )
62+ # Extract frontmatter (between first two --- markers)
63+ FRONTMATTER=$( sed -n ' /^---$/,/^---$/p' " $SKILL_MD " | sed ' 1d;$d' )
4164
42- if [ -z " $FRONTMATTER " ]; then
43- echo " ❌ Empty frontmatter"
44- (( ERRORS ++ ))
45- continue
46- fi
65+ if [ -z " $FRONTMATTER " ]; then
66+ echo " ❌ Empty frontmatter"
67+ (( RRORS ++ )) || true
68+ continue
69+ fi
4770
48- # Check required fields
49- HAS_NAME=false
50- HAS_DESC=false
51- HAS_LICENSE=false
71+ # Check required fields
72+ HAS_NAME=false
73+ HAS_DESC=false
74+ HAS_LICENSE=false
5275
53- # Check name field
54- if echo " $FRONTMATTER " | grep -q " ^name:" ; then
55- FRONT_NAME=$( echo " $FRONTMATTER " | grep " ^name:" | sed ' s/name:\s*//' | tr -d ' ' )
56- HAS_NAME=true
76+ # Check name field
77+ if echo " $FRONTMATTER " | grep -q " ^name:" ; then
78+ FRONT_NAME=$( echo " $FRONTMATTER " | grep " ^name:" | sed ' s/name:\s*//' | tr -d ' ' )
79+ HAS_NAME=true
5780
58- if [ " $FRONT_NAME " != " $SKILL_NAME " ]; then
59- echo " ❌ Frontmatter name '$FRONT_NAME ' doesn't match directory '$SKILL_NAME '"
60- (( ERRORS++ ))
81+ if [ " $FRONT_NAME " != " $SKILL_NAME " ]; then
82+ echo " ❌ Frontmatter name '$FRONT_NAME ' doesn't match directory '$SKILL_NAME '"
83+ (( RRORS++ )) || true
84+ else
85+ echo " ✅ Frontmatter name matches directory"
86+ fi
6187 else
62- echo " ✅ Frontmatter name matches directory"
88+ echo " ❌ Missing required field: name"
89+ (( RRORS++ )) || true
6390 fi
64- else
65- echo " ❌ Missing required field: name"
66- (( ERRORS++ ))
67- fi
6891
69- # Check description field
70- if echo " $FRONTMATTER " | grep -q " ^description:" ; then
71- DESC=$( echo " $FRONTMATTER " | grep " ^description:" | sed ' s/description:\s*//' )
72- HAS_DESC=true
73- DESC_LEN=${# DESC}
74-
75- if [ $DESC_LEN -lt 10 ]; then
76- echo " ❌ Description too short ($DESC_LEN chars, min 10)"
77- (( ERRORS++ ))
78- elif [ $DESC_LEN -gt 200 ]; then
79- echo " ⚠️ Description very long ($DESC_LEN chars, recommended max 200)"
80- (( WARNINGS++ ))
92+ # Check description field
93+ if echo " $FRONTMATTER " | grep -q " ^description:" ; then
94+ DESC=$( echo " $FRONTMATTER " | grep " ^description:" | sed ' s/description:\s*//' )
95+ HAS_DESC=true
96+ DESC_LEN=${# DESC}
97+
98+ if [ $DESC_LEN -lt 10 ]; then
99+ echo " ❌ Description too short ($DESC_LEN chars, min 10)"
100+ (( RRORS++ )) || true
101+ elif [ $DESC_LEN -gt 200 ]; then
102+ echo " ⚠️ Description very long ($DESC_LEN chars, recommended max 200)"
103+ (( WARNINGS++ )) || true
104+ else
105+ echo " ✅ Description length valid: $DESC_LEN chars"
106+ fi
81107 else
82- echo " ✅ Description length valid: $DESC_LEN chars"
108+ echo " ❌ Missing required field: description"
109+ (( RRORS++ )) || true
83110 fi
84- else
85- echo " ❌ Missing required field: description"
86- (( ERRORS++ ))
87- fi
88111
89- # Check license field
90- if echo " $FRONTMATTER " | grep -q " ^license:" ; then
91- LICENSE=$( echo " $FRONTMATTER " | grep " ^license:" | sed ' s/license:\s*//' | tr -d ' ' )
92- HAS_LICENSE=true
112+ # Check license field
113+ if echo " $FRONTMATTER " | grep -q " ^license:" ; then
114+ LICENSE=$( echo " $FRONTMATTER " | grep " ^license:" | sed ' s/license:\s*//' | tr -d ' ' )
115+ HAS_LICENSE=true
93116
94- # Check if license is from valid enum
95- VALID_LICENSES=" MIT Apache-2.0 GPL-3.0 BSD-3-Clause CC-BY-SA Unlicense"
96- if echo " $VALID_LICENSES " | grep -qw " $LICENSE " ; then
97- echo " ✅ License valid: $LICENSE "
117+ # Check if license is from valid enum
118+ VALID_LICENSES=" MIT Apache-2.0 GPL-3.0 BSD-3-Clause CC-BY-SA Unlicense"
119+ if echo " $VALID_LICENSES " | grep -qw " $LICENSE " ; then
120+ echo " ✅ License valid: $LICENSE "
121+ else
122+ echo " ⚠️ License '$LICENSE ' not in common list: $VALID_LICENSES "
123+ (( WARNINGS++ )) || true
124+ fi
98125 else
99- echo " ⚠️ License ' $LICENSE ' not in common list: $VALID_LICENSES "
100- (( WARNINGS ++ ))
126+ echo " ❌ Missing required field: license "
127+ (( RRORS ++ )) || true
101128 fi
102- else
103- echo " ❌ Missing required field: license"
104- (( ERRORS++ ))
105- fi
106129
107- # Check title heading
108- if grep -q " ^# SKILL: $SKILL_NAME " " $SKILL_MD " ; then
109- echo " ✅ Title heading matches skill name"
110- else
111- echo " ⚠️ Title heading doesn't match expected format: '# SKILL: $SKILL_NAME '"
112- (( WARNINGS++ ))
113- fi
130+ # Check title heading
131+ if grep -q " ^# SKILL: $SKILL_NAME " " $SKILL_MD " ; then
132+ echo " ✅ Title heading matches skill name"
133+ else
134+ echo " ⚠️ Title heading doesn't match expected format: '# SKILL: $SKILL_NAME '"
135+ (( WARNINGS++ )) || true
136+ fi
114137
115- echo " "
116- fi
117- done
138+ echo " "
139+ fi
140+ done
118141else
119142 echo " ℹ️ No skills/ directory found (skipping skill frontmatter validation)"
120143fi
@@ -125,7 +148,7 @@ if [ -d "$PLUGIN_DIR/commands" ]; then
125148 echo " Validating command frontmatter..."
126149 echo " "
127150
128- for cmd_file in " $PLUGIN_DIR /commands" /* .md ; do
151+ for cmd_file in " $PLUGIN_DIR /commands" /* .md; do
129152 if [ -f " $cmd_file " ]; then
130153 CMD_NAME=$( basename " $cmd_file " .md)
131154
@@ -134,7 +157,7 @@ if [ -d "$PLUGIN_DIR/commands" ]; then
134157 # Check for frontmatter block
135158 if ! grep -q " ^---$" " $cmd_file " ; then
136159 echo " ❌ No frontmatter found (missing --- markers)"
137- (( ERRORS ++ ))
160+ (( RRORS ++ )) || true
138161 continue
139162 fi
140163
@@ -143,7 +166,7 @@ if [ -d "$PLUGIN_DIR/commands" ]; then
143166
144167 if [ -z " $FRONTMATTER " ]; then
145168 echo " ❌ Empty frontmatter"
146- (( ERRORS ++ ))
169+ (( RRORS ++ )) || true
147170 continue
148171 fi
149172
@@ -154,16 +177,16 @@ if [ -d "$PLUGIN_DIR/commands" ]; then
154177
155178 if [ $DESC_LEN -lt 5 ]; then
156179 echo " ❌ Description too short ($DESC_LEN chars, min 5)"
157- (( ERRORS ++ ))
180+ (( RRORS ++ )) || true
158181 elif [ $DESC_LEN -gt 100 ]; then
159182 echo " ⚠️ Description very long ($DESC_LEN chars, recommended max 100)"
160- (( WARNINGS++ ))
183+ (( WARNINGS++ )) || true
161184 else
162185 echo " ✅ Description length valid: $DESC_LEN chars"
163186 fi
164187 else
165188 echo " ❌ Missing required field: description"
166- (( ERRORS ++ ))
189+ (( RRORS ++ )) || true
167190 fi
168191
169192 echo " "
0 commit comments