davila7/claude-code-templates · warning

Access denied

Error message

Access denied

What it means

HTTP 403 from the skill dashboard file route when the requested file path, after path.join + path.normalize, does not stay inside skill.path — i.e. a path traversal (../) attempt or a path that resolves outside the skill directory.

Source

Thrown at cli-tool/src/skill-dashboard.js:416

        const skillName = req.params.name;
        const filePath = req.params[0]; // Capture the wildcard path

        await this.loadSkillsData();
        const skill = this.skills.find(s =>
          s.name === skillName ||
          s.name.toLowerCase().replace(/\s+/g, '-') === skillName.toLowerCase()
        );

        if (!skill) {
          return res.status(404).json({ error: 'Skill not found' });
        }

        const fullPath = path.join(skill.path, filePath);

        // Security check: ensure the file is within the skill directory
        const normalizedPath = path.normalize(fullPath);
        if (!normalizedPath.startsWith(skill.path)) {
          return res.status(403).json({ error: 'Access denied' });
        }

        if (!(await fs.pathExists(fullPath))) {
          return res.status(404).json({ error: 'File not found' });
        }

        const content = await fs.readFile(fullPath, 'utf8');
        const stats = await fs.stat(fullPath);

        res.json({
          content,
          path: filePath,
          size: this.formatFileSize(stats.size),
          lastModified: stats.mtime,
          timestamp: new Date().toISOString()
        });
      } catch (error) {
        console.error('Error loading file:', error);

View on GitHub (pinned to a0851ed10c)

Solutions

  1. Request paths strictly relative to the skill root
  2. URL-encode the file path and avoid .. segments
  3. If legitimate file is rejected, verify skill.path is the expected directory

Example fix

// before
fetch(`/api/skills/${name}/file/../../config.json`);
// after
fetch(`/api/skills/${name}/file/${encodeURIComponent(relPath)}`);
Defensive patterns

Strategy: validation

Validate before calling

const safeRel = (p) => !p.split('/').includes('..') && !path.isAbsolute(p);

Type guard

const isSafeRelPath = (p) => typeof p === 'string' && !p.startsWith('/') && !p.split(/[\\/]/).includes('..');

Prevention

When it happens

Trigger: GET /api/skills/x/file/../../secrets.json, or absolute paths, or encoded traversal sequences that decode to ../ segments.

Common situations: Malicious or buggy frontend requests; manually crafted URLs probing the local server (it binds locally, but the guard still triggers).

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of davila7/claude-code-templates@a0851ed10c (2026-08-28). Data as JSON: /api/errors/fbb4671fae675614. Report an issue: GitHub.