HIGH container escapesails

Container Escape in Sails

How Container Escape Manifests in Sails

Container escape in Sails applications typically occurs through improper handling of file system operations and path traversal vulnerabilities. When Sails applications need to read configuration files, access user uploads, or serve static assets, they often construct file paths dynamically. If these paths aren't properly sanitized, an attacker can manipulate input to break out of the intended directory structure.

A common pattern in Sails involves using require() or fs.readFile() with user-controlled paths. For example, an API endpoint that reads configuration files based on a filename parameter might look like:

module.exports = {
  config: function (req, res) {
    const filename = req.query.filename || 'default.json';
    const configPath = path.join(__dirname, '../config/', filename);
    const config = require(configPath);
    res.json(config);
  }
};

The vulnerability here is that path.join() doesn't prevent path traversal. An attacker can request /api/config?filename=../../package.json to read files outside the intended directory. More sophisticated attacks use URL encoding like ..%2F..%2F to bypass simple filters.

Sails's Waterline ORM can also be exploited for container escape when combined with improper file handling. Consider an application that stores file paths in the database and later reads them:

module.exports = {
  download: function (req, res) {
    Upload.findOne(req.params.id).exec(function (err, upload) {
      if (err || !upload) return res.notFound();
      const filePath = upload.path; // User-controlled path from DB
      res.download(filePath);
    });
  }
};

If an attacker can influence the upload.path field through SQL injection or direct database manipulation, they can cause the application to read arbitrary files. This becomes a container escape when the application runs with elevated privileges or when the container's file system contains sensitive files.

Another Sails-specific scenario involves policy-based file serving. Sails policies might allow authenticated users to access certain directories, but if the policy doesn't properly validate the resolved path, traversal is possible:

module.exports = {
  allowed: function (req, res, next) {
    if (!req.user) return res.forbidden();
    const filePath = path.join('/var/app/uploads', req.query.file);
    // Missing path validation here!
    req.filePath = filePath;
    next();
  }
};

Even with authentication, this allows any authenticated user to read files anywhere on the container's file system that the application process can access.

Sails-Specific Detection

Detecting container escape vulnerabilities in Sails requires examining both code patterns and runtime behavior. Static analysis should focus on finding instances where user input influences file system operations without proper validation.

Code review should look for these specific patterns in Sails controllers and policies:

// Dangerous patterns to flag:
const filePath = path.join(baseDir, req.query.path);
const content = fs.readFileSync(filePath, 'utf8');
// No validation that filePath starts with baseDir

const filePath = path.resolve(req.query.path);
// path.resolve() can create absolute paths, bypassing containment

const filePath = __dirname + '/' + req.query.file;
// String concatenation is especially dangerous

middleBrick's API security scanner can detect these vulnerabilities by testing endpoints with path traversal payloads. For a Sails application, middleBrick would send requests like:

curl -X GET "http://your-sails-app.com/api/config?filename=../../package.json"
curl -X GET "http://your-sails-app.com/api/download?file=../../../../etc/passwd"

The scanner checks if the application returns file contents rather than proper error responses. middleBrick's black-box approach means it doesn't need access to your source code—it tests the actual runtime behavior of your deployed API endpoints.

For OpenAPI spec analysis, middleBrick examines your Swagger/OpenAPI definitions to identify endpoints that accept file paths or filenames as parameters. It then correlates this with its runtime scanning results to provide comprehensive coverage. The scanner tests 12 security categories including path traversal (related to container escape), authentication bypass, and data exposure.

Runtime detection should also include monitoring for unusual file access patterns. In a containerized environment, you can use auditd or similar tools to log file access attempts. Look for processes accessing files outside their expected directories, especially attempts to read /etc/passwd, /proc, or other system files.

Sails-Specific Remediation

Remediating container escape vulnerabilities in Sails requires a defense-in-depth approach. Start with proper path validation using Node.js's built-in path module:

const validatePath = (basePath, relativePath) => {
  const resolvedPath = path.resolve(basePath, relativePath);
  if (!resolvedPath.startsWith(basePath)) {
    throw new Error('Path traversal attempt detected');
  }
  return resolvedPath;
};

module.exports = {
  config: function (req, res) {
    try {
      const filename = req.query.filename || 'default.json';
      const configPath = validatePath(
        path.join(__dirname, '../config/'),
        filename
      );
      const config = require(configPath);
      res.json(config);
    } catch (err) {
      return res.status(400).json({ error: 'Invalid file request' });
    }
  }
};

For file serving, use Sails's built-in res.download() with validated paths, or better yet, use a whitelist approach:

const allowedFiles = ['config.json', 'settings.json', 'default.json'];

module.exports = {
  config: function (req, res) {
    const filename = req.query.filename || 'default.json';
    if (!allowedFiles.includes(filename)) {
      return res.status(400).json({ error: 'File not allowed' });
    }
    const configPath = path.join(__dirname, '../config/', filename);
    try {
      const config = require(configPath);
      res.json(config);
    } catch (err) {
      return res.status(500).json({ error: 'File read error' });
    }
  }
};

For database-driven file paths, implement strict validation before accessing the file system:

module.exports = {
  download: function (req, res) {
    Upload.findOne(req.params.id).exec(function (err, upload) {
      if (err || !upload) return res.notFound();
      
      const uploadDir = '/var/app/uploads/';
      const filePath = validatePath(uploadDir, upload.path);
      
      res.download(filePath, function (err) {
        if (err) {
          return res.status(404).json({ error: 'File not found' });
        }
      });
    });
  }
};

Container-level hardening is also essential. Run your Sails application with the least privileges necessary, use read-only file systems where possible, and implement AppArmor or SELinux profiles to restrict file system access. In your Dockerfile, avoid running as root and limit mounted volumes to only what's necessary:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs
EXPOSE 1337
CMD ["node", "server.js"]

Finally, implement comprehensive logging and monitoring. Log all file access attempts, especially those that fail validation, and set up alerts for suspicious patterns like repeated attempts to access system files or traverse directories.

Frequently Asked Questions

How can I test if my Sails application is vulnerable to container escape?
Use middleBrick's API security scanner to test your endpoints with path traversal payloads. The scanner will attempt to access files outside your application's directory structure and report if it successfully reads sensitive files. You can also manually test by sending requests with ../ sequences in file path parameters and checking if the server returns unexpected file contents.
Does container escape only affect applications running in containers?
No, while the term 'container escape' is commonly used in containerized environments, the underlying vulnerability—improper path validation leading to unauthorized file access—exists regardless of deployment method. The risk is higher in containers because they often run with elevated privileges and may contain sensitive configuration files, but traditional server deployments face similar risks if they don't validate file paths properly.