flipped-aurora/gin-vue-admin · warning

技能名称不合法

Error message

技能名称不合法

What it means

SkillsService.Detail validates the skill name with isSafeName before resolving its directory on disk. A name failing this check (empty, path separators, traversal like ../, or other unsafe characters) is rejected with this error to prevent path traversal outside the skills root.

Source

Thrown at server/service/system/sys_skills.go:92

	}
	entries, err := os.ReadDir(skillsDir)
	if err != nil {
		return nil, err
	}
	var skills []string
	for _, entry := range entries {
		if entry.IsDir() {
			skills = append(skills, entry.Name())
		}
	}
	sort.Strings(skills)
	return skills, nil
}

func (s *SkillsService) Detail(_ context.Context, tool, skill string) (system.SkillDetail, error) {
	var detail system.SkillDetail
	if !isSafeName(skill) {
		return detail, errors.New("技能名称不合法")
	}
	detail.Tool = tool
	detail.Skill = skill

	skillDir, err := s.skillDir(tool, skill)
	if err != nil {
		return detail, err
	}

	skillFilePath := filepath.Join(skillDir, skillFileName)
	content, err := os.ReadFile(skillFilePath)
	if err != nil {
		if !os.IsNotExist(err) {
			return detail, err
		}
		detail.Meta = system.SkillMeta{Name: skill}
		detail.Markdown = defaultSkillMarkdown
	} else {

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Pass only the bare skill directory name, without path separators or '..' segments.
  2. Trim and re-validate the skill value on the client before sending the request.
  3. If the name legitimately needs normalization, sanitize it server-side before calling Detail.

Example fix

// before
detail, err := skillsService.Detail(ctx, "claude", "../../etc")
// after
detail, err := skillsService.Detail(ctx, "claude", "my-skill")
Defensive patterns

Strategy: validation

Validate before calling

const safeNameRe = /^[A-Za-z0-9][A-Za-z0-9._-]*$/
if !safeNameRe.test(skill) throw new Error("invalid skill name: " + skill)
await api.getSkillDetail(tool, skill)

Type guard

func isSafeSkillName(s string) bool {
	if s == "" { return false }
	if strings.ContainsAny(s, "/\\") { return false }
	return !strings.Contains(s, "..")
}

Try / catch

try {
  await api.getSkillDetail(tool, skill)
} catch (e) {
  if (e.response?.data?.msg === "技能名称不合法") {
    showInputError("技能名称只能包含字母、数字、点、下划线和中划线")
  }
}

Prevention

When it happens

Trigger: Calling Detail(tool, skill) with a skill value that is empty or contains '/', '\\', '..', or other characters rejected by isSafeName.

Common situations: Client sends a skill name copied from a file path (e.g. 'skills/my-skill/SKILL.md'); crafted URLs attempting directory traversal; frontend sends empty skill param after a bad selection.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/97d68aedfe0245b2. Report an issue: GitHub.