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); }
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); } }
public override DekiScriptLiteral InvokeMap(DekiScriptRuntime runtime, DekiScriptMap args) { return InvokeHelper(runtime, DekiScriptParameter.ValidateToMap(Parameters, args)); }
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); }
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))); }
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); } }