示例#1
0
        protected override Yield Start(XDoc config, Result result) {
            yield return Coroutine.Invoke(base.Start, config, new Result());

            // init environment for script execution
            _runtime = new DekiScriptRuntime();
            _env = _runtime.CreateEnv();
            result.Return();
        }
        public object WebJson(
           [DekiExtParam("source text or source uri (default: none)", true)] string source,
           [DekiExtParam("caching duration in seconds (range: 300+; default: 300)", true)] double? ttl,
           DekiScriptRuntime runtime
        ) {
            source = source ?? string.Empty;
            XUri uri = XUri.TryParse(source);
            if(uri == null) {
                return DekiScriptLibrary.JsonParse(source, runtime);
            }

            // we need to fetch an online document
            string response = CachedWebGet(uri, ttl);
            return DekiScriptLibrary.JsonParse(response, runtime);
        }
示例#3
0
            private DekiScriptLiteral InvokeHelper(DekiScriptRuntime runtime, DekiScriptMap args) {

                // invoke script
                DekiScriptEnv env = _env.NewScope();
                env.Vars.AddRange(DreamContext.Current.GetState<DekiScriptMap>("env.implicit"));
                env.Vars.Add("args", args);
                env.Vars.Add("$", args);
                var result = runtime.Evaluate(Expression, DekiScriptEvalMode.Evaluate, env);
                try {
                    return result.Convert(_returnType);
                } catch(DekiScriptInvalidCastException) {
                    throw new DekiScriptInvalidReturnCastException(Location.None, result.ScriptType, _returnType);
                }
            }
示例#4
0
 public override DekiScriptLiteral InvokeMap(DekiScriptRuntime runtime, DekiScriptMap args) {
     return InvokeHelper(runtime, DekiScriptParameter.ValidateToMap(Parameters, args));
 }
示例#5
0
        private void LoadScript() {
            _manifestPath = null;
            _resourcesPath = null;
            _manifestUri = null;
            _resourcesUri = null;
            _manifest = null;

            // read manifest
            _manifestPath = Config["manifest"].AsText;
            if(string.IsNullOrEmpty(_manifestPath)) {
                throw new ArgumentNullException("manifest");
            }
            _manifestUri = XUri.TryParse(_manifestPath);
            if(_manifestUri != null) {
                _manifestPath = null;
                _manifest = Plug.New(_manifestUri).Get().ToDocument();
                _resourcesUri = Config["resources"].AsUri ?? _manifestUri.WithoutLastSegment();
            } else {
                _manifest = XDocFactory.LoadFrom(_manifestPath, MimeType.XML);
                _resourcesPath = Config["resources"].AsText ?? Path.GetDirectoryName(_manifestPath);
            }
            if(!_manifest.HasName("extension")) {
                throw new ArgumentException("invalid extension manifest");
            }

            // initilize runtime
            _runtime = new DekiScriptRuntime();

            // read manifest settings
            _title = _manifest["title"].AsText;
            _label = _manifest["label"].AsText;
            _copyright = _manifest["copyright"].AsText;
            _description = _manifest["description"].AsText;
            _help = _manifest["uri.help"].AsText;
            _logo = _manifest["uri.logo"].AsText;
            _namespace = _manifest["namespace"].AsText;

            // initialize evaluation environment
            _commonEnv = _runtime.CreateEnv();

            // read functions
            _functions = new Dictionary<XUri, DekiScriptInvocationTargetDescriptor>();
            foreach(var function in _manifest["function"]) {
                var descriptor = ConvertFunction(function);
                if(descriptor != null) {
                    var uri = Self.At(descriptor.SystemName);
                    DekiScriptInvocationTargetDescriptor old;
                    if(_functions.TryGetValue(uri, out old)) {
                        _log.WarnFormat("duplicate function name {0} in script {1}", descriptor.Name, _manifestUri);
                    }
                    _functions[uri] = descriptor;
                }
            }
            _runtime.RegisterExtensionFunctions(_functions);

            // add extension functions to env
            foreach(var function in _functions) {
                _commonEnv.Vars.AddNativeValueAt(function.Value.Name.ToLowerInvariant(), function.Key);
            }

            // add configuration settings
            DreamContext context = DreamContext.Current;
            DekiScriptMap scriptConfig = new DekiScriptMap();
            foreach(KeyValuePair<string, string> entry in Config.ToKeyValuePairs()) {
                XUri local;
                if(XUri.TryParse(entry.Value, out local)) {
                    local = context.AsPublicUri(local);
                    scriptConfig.AddAt(entry.Key.Split('/'), DekiScriptExpression.Constant(local.ToString()));
                } else {
                    scriptConfig.AddAt(entry.Key.Split('/'), DekiScriptExpression.Constant(entry.Value));
                }
            }
            _commonEnv.Vars.Add("config", scriptConfig);
        }
示例#6
0
 protected override Yield Stop(Result result) {
     _publicDigitalSignature = null;
     _manifestPath = null;
     _resourcesPath = null;
     _manifestUri = null;
     _resourcesUri = null;
     _manifest = null;
     _debug = false;
     _runtime = null;
     _commonEnv = null;
     _copyright = null;
     _description = null;
     _functions = null;
     _help = null;
     _label = null;
     _logo = null;
     _namespace = null;
     _title = null;
     yield return Coroutine.Invoke(base.Stop, new Result());
     result.Return();
 }
        //--- Class Methods ---
        public static XmlNode Evaluate(
            XDoc script,
            XmlElement node,
            DekiScriptEvalContext context,
            DekiScriptEnv env,
            DekiScriptRuntime runtime,
            out bool scripted,
            ref bool error
        ) {
            if((context.Mode != DekiScriptEvalMode.Verify) && (context.Mode != DekiScriptEvalMode.EvaluateEditOnly) && (context.Mode != DekiScriptEvalMode.EvaluateSaveOnly)) {
                throw new InvalidOperationException("DekiScript interpreter can only used for save, edit, or verify evaluations.");
            }
            scripted = false;
            XmlNode next = node.NextSibling;
            try {

                // check if element needs to be evaluated
                if(StringUtil.EqualsInvariant(node.NamespaceURI, XDekiScript.ScriptNS)) {
                    scripted = true;

                    // NOTE (steveb): <eval:xyz> nodes are not processed by the interpreter anymore

                } else {
                    XDoc current = script[node];
                    bool hasScriptClass = StringUtil.EqualsInvariant(node.GetAttribute("class"), "script");

                    #region <elem class="script" init="..." if="..." foreach="..." where="..." block="...">
                    // check if element has form <elem class="script" init="..." if="..." foreach="..." where="..." block="...">
                    scripted = node.HasAttribute("init") || node.HasAttribute("if") || node.HasAttribute("foreach") || node.HasAttribute("block");
                    if(context.Mode == DekiScriptEvalMode.Verify) {

                        // check if "block" is present
                        string blockAttr = node.GetAttribute("block");
                        if(!string.IsNullOrEmpty(blockAttr)) {

                            // TODO (steveb): validate script expression

                        }

                        // check if "foreach" is present
                        string foreachAttr = node.GetAttribute("foreach");
                        if(!string.IsNullOrEmpty(foreachAttr)) {

                            // TODO (steveb): validate script expression

                        }

                        // check if "if" is present
                        string ifAttr = node.GetAttribute("if");
                        if(!string.IsNullOrEmpty(ifAttr)) {

                            // TODO (steveb): validate script expression

                        }

                        // check if "init" is present
                        string initAttr = node.GetAttribute("init");
                        if(!string.IsNullOrEmpty(initAttr)) {

                            // TODO (steveb): validate script expression

                        }
                    }
                    #endregion

                    // evaluate child nodes
                    EvaluateChildren(script, node, context, env, runtime, out scripted, ref error);

                    #region evaluate attributes
                    for(int i = 0; i < node.Attributes.Count; ++i) {
                        XmlAttribute attribute = node.Attributes[i];

                        // check if attribute needs to be evaluated
                        if(attribute.NamespaceURI == XDekiScript.ScriptNS) {
                            scripted = true;

                            // NOTE (steveb): eval:xyz="abc" attributes are not processed by the interpreter anymore

                        } else if(StringUtil.StartsWithInvariant(attribute.Value, "{{") && StringUtil.EndsWithInvariant(attribute.Value, "}}")) {
                            scripted = true;

                            // NOTE (steveb): key="{{value}}"
                            string code = attribute.Value.Substring(2, attribute.Value.Length - 4).Trim();

                            // check if script content is substituted
                            bool isPermanentReplacement = false;
                            if(StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN)) {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                                code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length);
                            } else if(StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN)) {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                                code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length);
                            } else if(StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN)) {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly);
                                code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length);
                            }

                            // parse expression
                            if((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement) {
                                DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(attribute), code);
                                if(isPermanentReplacement) {
                                    DekiScriptLiteral eval = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env);

                                    // determine what the outcome value is
                                    string value = eval.AsString();

                                    // check if we have a value to replace the current attribute with
                                    if((value != null) && !DekiScriptLibrary.ContainsXSSVulnerability(attribute.LocalName, value)) {
                                        attribute.Value = value;
                                    } else {
                                        node.Attributes.RemoveAt(i);
                                        --i;
                                    }
                                }
                            }
                        }
                    }
                    #endregion

                    #region evaluate <span class="script"> or <pre class="script">
                    if(hasScriptClass && (StringUtil.EqualsInvariant(node.LocalName, "pre") || StringUtil.EqualsInvariant(node.LocalName, "span")) && !node.HasAttribute("function")) {

                        // replace the non-breaking space character with space
                        string code = node.InnerText.ReplaceAll("\u00A0", " ", "\u00AD", "").Trim();

                        // check if script content is substituted
                        bool isPermanentReplacement = false;
                        if(StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN)) {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                            code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length);
                        } else if(StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN)) {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                            code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length);
                        } else if(StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN)) {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly);
                            code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length);
                        }

                        // parse expression
                        if((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement) {
                            DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(node), code);
                            if(isPermanentReplacement) {
                                DekiScriptLiteral value = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env);
                                context.ReplaceNodeWithValue(node, value);
                            }
                            if(!isPermanentReplacement) {
                                scripted = true;
                            }
                        }
                    }
                    #endregion
                }
            } catch(Exception e) {

                // only embed error in verify mode, not in save/edit modes
                if(context.Mode == DekiScriptEvalMode.Verify) {
                    context.InsertExceptionMessageBeforeNode(env, node.ParentNode, node, ComputeNodeLocation(node), e);
                    node.ParentNode.RemoveChild(node);
                }
                error |= true;
            }
            return next;
        }
        private static void EvaluateChildren(
            XDoc script,
            XmlElement node,
            DekiScriptEvalContext context,
            DekiScriptEnv env,
            DekiScriptRuntime runtime,
            out bool scripted,
            ref bool error
        ) {
            scripted = false;

            // recurse first to evaluate nested script content
            XmlNode child = node.FirstChild;
            while(child != null) {
                XmlNode next = child.NextSibling;
                if(child.NodeType == XmlNodeType.Element) {
                    bool childScripted;
                    next = Evaluate(script, (XmlElement)child, context, env, runtime, out childScripted, ref error);
                    scripted = scripted || childScripted;
                }
                child = next;
            }
        }
        //--- Class Methods ---
        public static XmlNode Evaluate(
            XDoc script,
            XmlElement node,
            DekiScriptEvalContext context,
            DekiScriptEnv env,
            DekiScriptRuntime runtime,
            out bool scripted,
            ref bool error
            )
        {
            if ((context.Mode != DekiScriptEvalMode.Verify) && (context.Mode != DekiScriptEvalMode.EvaluateEditOnly) && (context.Mode != DekiScriptEvalMode.EvaluateSaveOnly))
            {
                throw new InvalidOperationException("DekiScript interpreter can only used for save, edit, or verify evaluations.");
            }
            scripted = false;
            XmlNode next = node.NextSibling;

            try {
                // check if element needs to be evaluated
                if (StringUtil.EqualsInvariant(node.NamespaceURI, XDekiScript.ScriptNS))
                {
                    scripted = true;

                    // NOTE (steveb): <eval:xyz> nodes are not processed by the interpreter anymore
                }
                else
                {
                    XDoc current        = script[node];
                    bool hasScriptClass = StringUtil.EqualsInvariant(node.GetAttribute("class"), "script");

                    #region <elem class="script" init="..." if="..." foreach="..." where="..." block="...">
                    // check if element has form <elem class="script" init="..." if="..." foreach="..." where="..." block="...">
                    scripted = node.HasAttribute("init") || node.HasAttribute("if") || node.HasAttribute("foreach") || node.HasAttribute("block");
                    if (context.Mode == DekiScriptEvalMode.Verify)
                    {
                        // check if "block" is present
                        string blockAttr = node.GetAttribute("block");
                        if (!string.IsNullOrEmpty(blockAttr))
                        {
                            // TODO (steveb): validate script expression
                        }

                        // check if "foreach" is present
                        string foreachAttr = node.GetAttribute("foreach");
                        if (!string.IsNullOrEmpty(foreachAttr))
                        {
                            // TODO (steveb): validate script expression
                        }

                        // check if "if" is present
                        string ifAttr = node.GetAttribute("if");
                        if (!string.IsNullOrEmpty(ifAttr))
                        {
                            // TODO (steveb): validate script expression
                        }

                        // check if "init" is present
                        string initAttr = node.GetAttribute("init");
                        if (!string.IsNullOrEmpty(initAttr))
                        {
                            // TODO (steveb): validate script expression
                        }
                    }
                    #endregion

                    // evaluate child nodes
                    EvaluateChildren(script, node, context, env, runtime, out scripted, ref error);

                    #region evaluate attributes
                    for (int i = 0; i < node.Attributes.Count; ++i)
                    {
                        XmlAttribute attribute = node.Attributes[i];

                        // check if attribute needs to be evaluated
                        if (attribute.NamespaceURI == XDekiScript.ScriptNS)
                        {
                            scripted = true;

                            // NOTE (steveb): eval:xyz="abc" attributes are not processed by the interpreter anymore
                        }
                        else if (StringUtil.StartsWithInvariant(attribute.Value, "{{") && StringUtil.EndsWithInvariant(attribute.Value, "}}"))
                        {
                            scripted = true;

                            // NOTE (steveb): key="{{value}}"
                            string code = attribute.Value.Substring(2, attribute.Value.Length - 4).Trim();

                            // check if script content is substituted
                            bool isPermanentReplacement = false;
                            if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN))
                            {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                                code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length);
                            }
                            else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN))
                            {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                                code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length);
                            }
                            else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN))
                            {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly);
                                code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length);
                            }

                            // parse expression
                            if ((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement)
                            {
                                DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(attribute), code);
                                if (isPermanentReplacement)
                                {
                                    DekiScriptLiteral eval = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env);

                                    // determine what the outcome value is
                                    string value = eval.AsString();

                                    // check if we have a value to replace the current attribute with
                                    if ((value != null) && !DekiScriptLibrary.ContainsXSSVulnerability(attribute.LocalName, value))
                                    {
                                        attribute.Value = value;
                                    }
                                    else
                                    {
                                        node.Attributes.RemoveAt(i);
                                        --i;
                                    }
                                }
                            }
                        }
                    }
                    #endregion

                    #region evaluate <span class="script"> or <pre class="script">
                    if (hasScriptClass && (StringUtil.EqualsInvariant(node.LocalName, "pre") || StringUtil.EqualsInvariant(node.LocalName, "span")) && !node.HasAttribute("function"))
                    {
                        // replace the non-breaking space character with space
                        string code = node.InnerText.ReplaceAll("\u00A0", " ", "\u00AD", "").Trim();

                        // check if script content is substituted
                        bool isPermanentReplacement = false;
                        if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN))
                        {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                            code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length);
                        }
                        else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN))
                        {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                            code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length);
                        }
                        else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN))
                        {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly);
                            code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length);
                        }

                        // parse expression
                        if ((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement)
                        {
                            DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(node), code);
                            if (isPermanentReplacement)
                            {
                                DekiScriptLiteral value = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env);
                                context.ReplaceNodeWithValue(node, value);
                            }
                            if (!isPermanentReplacement)
                            {
                                scripted = true;
                            }
                        }
                    }
                    #endregion
                }
            } catch (Exception e) {
                // only embed error in verify mode, not in save/edit modes
                if (context.Mode == DekiScriptEvalMode.Verify)
                {
                    context.InsertExceptionMessageBeforeNode(env, node.ParentNode, node, ComputeNodeLocation(node), e);
                    node.ParentNode.RemoveChild(node);
                }
                error |= true;
            }
            return(next);
        }
 //--- Extension Methods ---
 public static DekiScriptExpression Optimize(this DekiScriptRuntime runtime, DekiScriptExpression expr, DekiScriptEvalMode mode, DekiScriptEnv env)
 {
     return(expr.VisitWith(DekiScriptExpressionOptimizer.Instance, new DekiScriptExpressionEvaluationState(mode, env, runtime, TimeSpan.MaxValue, int.MaxValue)));
 }
示例#11
0
        private void InitScriptRuntimeAndEnv() {
            if(_runtime != null) {
                return;
            }

            // Note (arnec): have to lazily init the runtime and env, because they require instance settings and they currently have
            // a requirement of DekiContext.Current.Instance being set, i.e. a race condition in the ctor
            lock(SettingsSyncRoot) {
                if(_runtime != null) {
                    return;
                }
                _runtime = new ExtensionRuntime(new DekiInstanceSettings());
                _scriptEnv = _deki.InitializeRuntimeAndEnvironment(_runtime);
            }
        }