File cf36780b-Implement-import-recursion-check.patch of Package libsass.7438
From cf36780bce7f0258c39181b14e0e749dfd669ce3 Mon Sep 17 00:00:00 2001
From: Marcel Greter <marcel.greter@ocbnet.ch>
Date: Wed, 6 Jan 2016 20:41:36 +0100
Subject: [PATCH] Implement import recursion check
Also adds import stack reports on errors.
---
src/context.cpp | 28 ++++++++++++++++++++++++----
src/context.hpp | 2 +-
src/error_handling.cpp | 9 +++++----
src/error_handling.hpp | 5 +++--
src/sass_context.cpp | 17 ++++++++++++++---
5 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/src/context.cpp b/src/context.cpp
index 7c74a6d9..eb5e0d63 100644
--- a/src/context.cpp
+++ b/src/context.cpp
@@ -28,6 +28,7 @@
#include "extend.hpp"
#include "remove_placeholders.hpp"
#include "functions.hpp"
+#include "sass_functions.hpp"
#include "backtrace.hpp"
#include "sass2scss.h"
#include "prelexer.hpp"
@@ -254,7 +255,7 @@ namespace Sass {
// register include with resolved path and its content
// memory of the resources will be freed by us on exit
- void Context::register_resource(const Include& inc, const Resource& res)
+ void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate)
{
// do not parse same resource twice
@@ -297,6 +298,24 @@ namespace Sass {
strings.push_back(sass_strdup(inc.abs_path.c_str()));
// create the initial parser state from resource
ParserState pstate(strings.back(), contents, idx);
+
+ // check existing import stack for possible recursion
+ for (size_t i = 0; i < import_stack.size() - 2; ++i) {
+ auto parent = import_stack[i];
+ if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
+ std::string stack("An @import loop has been found:");
+ for (size_t n = 1; n < i + 2; ++n) {
+ stack += "\n " + std::string(import_stack[n]->imp_path) +
+ " imports " + std::string(import_stack[n+1]->imp_path);
+ }
+ // implement error throw directly until we
+ // decided how to handle full stack traces
+ ParserState state = prstate ? *prstate : pstate;
+ throw Exception::InvalidSyntax(state, stack, &import_stack);
+ // error(stack, prstate ? *prstate : pstate, import_stack);
+ }
+ }
+
// create a parser instance from the given c_str buffer
Parser p(Parser::from_c_str(contents, *this, pstate));
// do not yet dispose these buffers
@@ -344,7 +363,7 @@ namespace Sass {
// the memory buffer returned must be freed by us!
if (char* contents = read_file(resolved[0].abs_path)) {
// register the newly resolved file resource
- register_resource(resolved[0], { contents, 0 });
+ register_resource(resolved[0], { contents, 0 }, &pstate);
// return resolved entry
return resolved[0];
}
@@ -433,7 +452,7 @@ namespace Sass {
// handle error message passed back from custom importer
// it may (or may not) override the line and column info
if (const char* err_message = sass_import_get_error_message(include)) {
- if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });
+ if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate);
if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);
else error(err_message, ParserState(ctx_path, source, Position(line, column)));
}
@@ -447,7 +466,7 @@ namespace Sass {
// attach information to AST node
imp->incs().push_back(include);
// register the resource buffers
- register_resource(include, { source, srcmap });
+ register_resource(include, { source, srcmap }, &pstate);
}
// only a path was retuned
// try to load it like normal
@@ -639,6 +658,7 @@ namespace Sass {
Expand expand(*this, &global, &backtrace);
Cssize cssize(*this, &backtrace);
// expand and eval the tree
+ // debug_ast(root);
root = root->perform(&expand)->block();
// merge and bubble certain rules
root = root->perform(&cssize)->block();
diff --git a/src/context.hpp b/src/context.hpp
index d81d5435..1628d5c6 100644
--- a/src/context.hpp
+++ b/src/context.hpp
@@ -92,7 +92,7 @@ namespace Sass {
virtual char* render(Block* root);
virtual char* render_srcmap();
- void register_resource(const Include&, const Resource&);
+ void register_resource(const Include&, const Resource&, ParserState* = 0);
std::vector<Include> find_includes(const Importer& import);
Include load_import(const Importer&, ParserState pstate);
diff --git a/src/error_handling.cpp b/src/error_handling.cpp
index 6e49f4be..389939a8 100644
--- a/src/error_handling.cpp
+++ b/src/error_handling.cpp
@@ -10,9 +10,10 @@ namespace Sass {
namespace Exception {
- Base::Base(ParserState pstate, std::string msg)
+ Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
: std::runtime_error(msg),
- msg(msg), pstate(pstate)
+ msg(msg), pstate(pstate),
+ import_stack(import_stack)
{ }
const char* Base::what() const throw()
@@ -44,8 +45,8 @@ namespace Sass {
msg += " for `" + fn + "'";
}
- InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg)
- : Base(pstate, msg)
+ InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
+ : Base(pstate, msg, import_stack)
{ }
}
diff --git a/src/error_handling.hpp b/src/error_handling.hpp
index aa08c7c3..0c2b1182 100644
--- a/src/error_handling.hpp
+++ b/src/error_handling.hpp
@@ -19,8 +19,9 @@ namespace Sass {
std::string msg;
public:
ParserState pstate;
+ std::vector<Sass_Import_Entry>* import_stack;
public:
- Base(ParserState pstate, std::string msg = def_msg);
+ Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0);
virtual const char* what() const throw();
virtual ~Base() throw() {};
};
@@ -53,7 +54,7 @@ namespace Sass {
class InvalidSyntax : public Base {
public:
- InvalidSyntax(ParserState pstate, std::string msg);
+ InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0);
virtual ~InvalidSyntax() throw() {};
};
diff --git a/src/sass_context.cpp b/src/sass_context.cpp
index 32e6b83e..3c644409 100644
--- a/src/sass_context.cpp
+++ b/src/sass_context.cpp
@@ -10,6 +10,7 @@
#include "util.hpp"
#include "context.hpp"
#include "sass_context.hpp"
+#include "sass_functions.hpp"
#include "ast_fwd_decl.hpp"
#include "error_handling.hpp"
@@ -41,7 +42,6 @@ extern "C" {
catch (Exception::Base& e) {
std::stringstream msg_stream;
std::string cwd(Sass::File::get_cwd());
- std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
std::string msg_prefix("Error: ");
bool got_newline = false;
@@ -60,8 +60,19 @@ extern "C" {
++ msg;
}
if (!got_newline) msg_stream << "\n";
- msg_stream << std::string(msg_prefix.size(), ' ');
- msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";
+ if (e.import_stack) {
+ for (size_t i = 1; i < e.import_stack->size() - 1; ++i) {
+ std::string path((*e.import_stack)[i]->imp_path);
+ std::string rel_path(Sass::File::abs2rel(path, cwd, cwd));
+ msg_stream << std::string(msg_prefix.size(), ' ');
+ msg_stream << (i == 1 ? " on line " : " from line ");
+ msg_stream << e.pstate.line+1 << " of " << rel_path << "\n";
+ }
+ } else {
+ std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
+ msg_stream << std::string(msg_prefix.size(), ' ');
+ msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";
+ }
// now create the code trace (ToDo: maybe have util functions?)
if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) {
--
2.13.2