2014-06-02

HTML imports and XSS

This email thread raises a good point: If you're doing any content filtering to prevent XSS attacks, make sure you check for <link rel="import ...> tags and make sure you're looking at the content being imported. HTML Imports *shouldn't* be any more risky than arbitrary script tags but given that the HTML import can pull in script, stylesheets and markup, you need to be careful. Best advice: treat import links exactly as you would any untrusted executable content.

2014-05-20

Activity Streams 2.0 and AppLinks

Recently I was asked how Activity Streams 2.0 Action Handlers compare with AppLinks.

For those who are not familiar, AppLinks is an emerging convention for using metadata embedded in HTML to provide "deep links" into mobile applications. You can catch the snazzy overview video on their website. Below is an example taken from their documentation:


<html>
<head>
    <meta property="al:ios:url" content="applinks://docs" />
    <meta property="al:ios:app_store_id" content="12345" />
    <meta property="al:ios:app_name" content="App Links" />
    <meta property="al:android:url" content="applinks://docs" />
    <meta property="al:android:app_name" content="App Links" />
    <meta property="al:android:package" content="org.applinks" />
    <meta property="al:web:url"
          content="http://applinks.org/documentation" />
</head>
<body>
Hello, world!
</body>
</html>

The basic idea is: whenever you want to link from one app to another, the linking app would do an HTTP GET to fetch this bit of HTML, parse out the metadata, check to see if the native app exists, and if it does, invoke it directly using the provided information. If the app doesn't exist, however, the browser is launched as a fallback. Pretty simple.

So how does this compare to Activity Streams 2.0 Actions? Well, that's pretty simple too: They are orthogonal to one another and completely compatible. We can actually take the AppLinks metadata elements and drop them directly into an Activity Streams 2.0 document. Following are two examples of this. I've created a fictional "AppLinks" objectType for the sake of the example. In the first case, AppLinks metadata is specified as an Action Handler. How this works ought to be fairly straightforward. In the second case, we use an Activity Streams 2.0 Link Value to embed the AppLinks metadata directly. Applications that don't know anything about AppLinks can safely ignore the additional markup.


{
  "objectType": "note",
  "content": "This is a note",
  "actions": {
    "view": {
      "objectType": "AppLinks",
      "al:ios:url": "applinks://docs",
      "al:ios:app_store_id": "12345",
      "al:ios:app_name": "App Links",
      "al:android:url": "applinks://docs",
      "al:android:app_name": "App Links",
      "al:android:package": "org.applinks",
      "al:web:url": "http://applinks.org/documentation"
    }
  }
}

{
  "objectType": "note",
  "content": "This is a note",
  "url": {
    "objectType": "AppLinks",
    "al:ios:url": "applinks://docs",
    "al:ios:app_store_id": "12345",
    "al:ios:app_name": "App Links",
    "al:android:url": "applinks://docs",
    "al:android:app_name": "App Links",
    "al:android:package": "org.applinks",
    "url": "http://applinks.org/documentation"   
  }
}

Simple and elegant. The way it should be.

2014-05-19

Towards a "default initial context" for AS 2.0 + JSON-LD

Just uploaded another iteration on the Activity Streams 2.0 JSON-LD @context. This takes a step towards establishing a default initial context for AS 2.0 that includes terms from the Dublin Core, FOAF, vCard, Org, Provenance, and Link Relations ontologies. While processing AS 2.0 as JSON-LD is entirely optional, this initial context ought to make it significantly easier to mix in these other vocabularies should you choose the JSON-LD approach. The updated @context is hosted here: https://w3id.org/activity-streams/v2

For example, using this context you could do something like:


{
  "@context": "https://w3id.org/activity-streams/v2",
  "objectType": {
    "id": "foaf:Person",
    "displayName": "Person"
  },
  "foaf:givenName": "James",
  "foaf:familyName": "Snell",
  "org:memberOf": "http://ibm.com",
  "prov:actingOnBehalfOf": "http://ibm.com",
  "about": "http://www.chmod777self.com"
}

The N-Quads produced would be:


<http://xmlns.com/foaf/0.1/Person> <http://activitystrea.ms/2.0/displayName> "Person" .
_:b0 <http://activitystrea.ms/2.0/objectType> <http://xmlns.com/foaf/0.1/Person> .
_:b0 <http://www.iana.org/assignments/link-relations/about> <http://www.chmod777self.com> .
_:b0 <http://www.w3.org/ns/org#memberOf> <http://ibm.com> .
_:b0 <http://www.w3.org/ns/prov#actingOnBehalfOf> <http://ibm.com> .
_:b0 <http://xmlns.com/foaf/0.1/familyName> "Snell" .
_:b0 <http://xmlns.com/foaf/0.1/givenName> "James" .

2014-05-16

IANA Link Relations JSON-LD Context

Useful new tool of the day: A simple Node app that generates a basic JSON-LD @context covering the current contents of the IANA Registry of Link Relations.

URL: http://linkrels.ng.bluemix.net/links (hosted on IBM's bluemix)

IANA: http://www.iana.org/assignments/link-relations/link-relations.xhtml

Source: https://github.com/jasnell/linkrels

Example (view using the JSON-LD Playground:


{
  "@context": "http://linkrels.ng.bluemix.net/links",
  "first": "http://example.org/first",
  "next": "http://example.org/next",
  "prev": "http://example.org/prev",
  "up": "http://example.org/up"
}

Results in the following N-Quads:


_:b0 <http://www.iana.org/assignments/link-relations/first> <http://example.org/first> .
_:b0 <http://www.iana.org/assignments/link-relations/next> <http://example.org/next> .
_:b0 <http://www.iana.org/assignments/link-relations/prev> <http://example.org/prev> .
_:b0 <http://www.iana.org/assignments/link-relations/up> <http://example.org/up> .

2014-05-13

Activity Streams 2.0 Action Handlers, Draft -06

A new draft of the Activity Streams 2.0 Action Handlers specification has been posted. This draft makes a number of important updates:

  • It introduces a new ViewActionHandler and draws a clear differentiation between HttpActionHandlers, ViewActionHandlers and EmbedActionHandlers, linking each back to the concept of Browser Contexts in HTML5, specifically:
    • HttpActionHandler is used primarily for REST/HTTP API request/responses. Invoking an HttpActionHandler has no bearing on the navigation of any browser context.
    • ViewActionHandlers are used for navigation. If you want the invocation of the action handler to result in a new popup browser window, or a new tab (generally any case where an existing or new browser context is navigated to a new location), you'd use ViewActionHandler.
    • EmbedActionHandlers are used for embedded content inline. That content could take the form of images, media files, a gadget, etc. A new subordinate browser context (i.e. an iframe) might be created.
  • It adds an HTML5 "sandbox" attribute to both the ViewActionHandler and EmbedActionHandler. This operates in exactly the same way as the sandbox attribute on HTML5 iframes. Whenever sandbox is used on an EmbedActionHandler, a new browser context (iframe) is created.
  • It defines how Action Handlers interact with the standard W3C Content Security Policy (CSP).
  • It changes the default action handler from HttpActionHandler to ViewActionHandler to properly reflect the most common use case.
  • It defines how the Base URI is established for Action Handlers as they relate to the Content Security Policy (i.e. how we determine the Origin of the Action Handler)

For example, suppose I have the following Activity Streams 2.0 object:


{
  "objectType": "note",
  "content": "Isn't this fun?",
  "actions": {
    "share": [
      {
        "objectType": "ViewActionHandler",
        "displayName": "Share Me!",
        "url": "http://example.org/share?id=123",
        "target": "_new"
      },
      {
        "objectType": "HttpActionHandler",
        "url": "http://api.example.org/share?id=123",
        "method": "POST"
      },
      {
        "objectType": "EmbedActionHandler",
        "content": "<div>...</div>",
        "mediaType": "text/html",
        "sandbox": "allow-scripts"
      }
    ]
  }
}

Here we have a note with a "share" action that has three possible Action Handlers. The ViewActionHandler is equivalent to a link. One possible way to implement the handler would be to translate it into something like: <a href="http://example.org/share?id=123">Share Me!</a>. The HttpActionHandler is equivalent to performing an XMLHttpRequest operation in the backend. The EmbedActionHandler would display the value of the content attribute in a sandboxed iframe that allows scripts to execute.

As before, the Action Handlers specification is still a work in progress so things may continue to evolve. Feedback is always welcome!

2014-05-02

More on Activity Streams 2.0 and schema.org/Actions

In my previous note, I explored ways in which we could map the Activity Streams 2.0 model into the schema.org model... here I wanted to briefly talk about going the other direction.. that is, how can be map a schema.org/Action to Activity Streams 2.0. It's fairly trivial using a JSON-LD context like the following:


{
  "@context": [
    {"@vocab":"http://schema.org/"}, {
    "as": "http://activitystrea.ms/2.0/",
    "actionStatus": {
      "@id": "as:status",
      "@type": "@vocab"
    },
    "agent": {
      "@id": "as:actor",
      "@type": "@id",
      "@container": "@set"
    },
    "instrument": {
      "@id": "as:instrument",
      "@type": "@id",
      "@container": "@set"
    },
    "location": {
      "@id": "as:location",
      "@type": "@id",
      "@container": "@set"
    },
    "object": {
      "@id": "as:object",
      "@type": "@id",
      "@container": "@set"
    },
    "participant": {
      "@id": "as:participant",
      "@type": "@id",
      "@container": "@set"
    },
    "result": {
      "@id": "as:result",
      "@type": "@id",
      "@container": "@set"
    },
    "image": {
      "@id": "as:image",
      "@type": "@id",
      "@container": "@set"
    },
    "sameAs": {
      "@id": "as:duplicates",
      "@type": "@id",
      "@container": "@set"
    },
    "url": {
      "@id": "as:url",
      "@type": "@id",
      "@container": "@set"
    },
    "name": {
      "@id": "as:displayName",
      "@container": "@language"
    },
    "description": {
      "@id": "as:summary",
      "@container": "@language"
    },
    "endTime": "as:endTime",
    "startTime": "as:startTime",
    "CompletedActionStatus": {
      "@id": "as:CompletedStatus"
    },
    "ActiveActionStatus": {
      "@id": "as:ActiveStatus"
    },
    "PotentialActionStatus": {
      "@id": "as:TentativeStatus"
    }
  }]
}

Given this context, the following bit of schema.org compliant JSON-LD:


{
  "@type": "WatchAction",
  "agent": {
    "@type": "Person",
    "name": "Sally"
  },
  "object": {
    "@type": "Movie",
    "name": "A Bug's Life"
  }
}

Yields the N-Quads:


_:c14n0 <http://activitystrea.ms/2.0/displayName> "Sally" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:c14n1 <http://activitystrea.ms/2.0/actor> _:c14n0 .
_:c14n1 <http://activitystrea.ms/2.0/object> _:c14n2 .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/WatchAction> .
_:c14n2 <http://activitystrea.ms/2.0/displayName> "A Bug's Life" .
_:c14n2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Movie> .

The compacted JSON-LD form becomes:


{
  "@type": "http://schema.org/WatchAction",
  "http://activitystrea.ms/2.0/actor": {
    "@type": "http://schema.org/Person",
    "http://activitystrea.ms/2.0/displayName": "Sally"
  },
  "http://activitystrea.ms/2.0/object": {
    "@type": "http://schema.org/Movie",
    "http://activitystrea.ms/2.0/displayName": "A Bug's Life"
  }
}

Which is easily translatable into the regular Activity Streams 2.0 syntax by walking backwards using the AS 2.0 JSON-LD context definition. The result, of course, is:


{
  "objectType": "http://schema.org/WatchAction",
  "actor": {
    "objectType": "http://schema.org/Person",
    "displayName": "Sally"
  },
  "object": {
    "objectType": "http://schema.org/Movie",
    "displayName": "A Bug's Life"
  }
}

2014-04-30

Activity Streams 2.0 and schema.org/Actions

Recently it was announced that "potential actions" have been introduced into the schema.org mix. A potential action is simply an Activity that hasn't yet occurred. Here's an example borrowed from the potential actions overview document:


{ 
 "@context": "http://schema.org", 
 "@type": "WatchAction", 
 "actionStatus": "CompletedActionStatus", 
 "agent" : { 
   "@type": "Person", 
   "name": "Kevin Bacon" 
 }, 
 "object" : { 
   "@type": "Movie", 
   "name": "Footloose" 
 }, 
 "startTime" : "2014-03-01" 
}

The first thing that ought to catch your attention is how strikingly familiar this example is to the basic Activity Streams format. Why it was decided to use different labels to express the same content, I have no idea, but since everything leverages JSON-LD, we can easily express the exact same bit of information using the Activity Streams 2.0 format and a little JSON-LD magic:


{
  "verb": "http://schema.org/WatchAction",  
  "actor" : { 
    "objectType": "http://schema.org/Person", 
    "displayName": "Kevin Bacon" 
  }, 
  "object" : { 
    "objectType": "http://schema.org/Movie", 
    "displayName": "Footloose" 
  }, 
  "startTime" : "2014-03-01",
  "status": "completed",
  "@context": {
    "verb": "@type",
    "s": "http://schema.org/",
    "actor": "s:agent",
    "object": "s:object",
    "startTime": "s:startTime",
    "objectType": "@type",
    "displayName": "s:name",
    "status": {
      "@id":"s:actionStatus",
      "@type": "@vocab"
    },
    "completed": "s:CompletedActionStatus"
  }
}

This yields the N-Quads:


_:c14n0 <http://schema.org/actionStatus> <http://schema.org/CompletedActionStatus> .
_:c14n0 <http://schema.org/agent> _:c14n2 .
_:c14n0 <http://schema.org/object> _:c14n1 .
_:c14n0 <http://schema.org/startTime> "2014-03-01" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/WatchAction> .
_:c14n1 <http://schema.org/name> "Footloose" .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Movie> .
_:c14n2 <http://schema.org/name> "Kevin Bacon" .
_:c14n2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .

Notice that, in this example, I did not pull in the experimental Activity Streams 2.0 context definition. That's because I want to interpret the fields as http://schema.org/* properties and not http://activitystrea.ms/2.0/* properties. It's an important distinction to make. I could easily mix the two vocabularies by including both context descriptions but we just don't need it for our purposes here.

Here's a more complex example taken from the potential actions overview:


{ 
  "@context": "http://schema.org", 
  "@type": "Restaurant", 
  "name": "Tartine Bakery", 
  "potentialAction": { 
    "@type": "ViewAction", 
    "target": [ 
      "http://www.urbanspoon.com/r/6/92204", 
      { 
        "@type": "EntryPoint", 
        "urlTemplate": "http://api.urbanspoon.com/r/6/92204", 
        "contentType": "application/json+ld" 
      }, 
      "android-app://com.urbanspoon/http/www.urbanspoon.com/r/6/92204", 
      { 
        "@type": "EntryPoint", 
        "urlTemplate": "urbanspoon://r/6/92204", 
        "application": { 
          "@type": "SoftwareApplication", 
          "@id": "284708449", 
          "name": "Urbanspoon iPhone & iPad App", 
          "operatingSystem": "iOS"
        }
      },
      { 
        "@type": "EntryPoint", 
        "urlTemplate": "urbanspoon://r/6/92204", 
        "application": { 
          "@type": "SoftwareApplication", 
          "@id": "5b23b738-bb64-4829-9296-5bcb59bb0d2d", 
          "name": "Windows Phone App", 
          "operatingSystem": "Windows Phone 8" 
       } 
      } 
    ] 
  } 
} 

In Activity Streams 2.0, we have the "actions" property which serves the same basic role as the new Schema.org "potentialAction". Unfortunately, however, there are enough differences in the model that we can't simply mask over them using a cleverly designed @context definition. But we have a few options.

The first option, of course, is to simply use "potentialAction" directly...


{ 
  "@context": {
    "s": "http://schema.org/",
    "objectType": "@type",
    "displayName": "s:name",
    "potentialAction": "s:potentialAction"
  }, 
  "objectType": "http://schema.org/Restaurant", 
  "displayName": "Tartine Bakery", 
  "potentialAction": { 
    "@type": "ViewAction", 
    "target": [ 
      "http://www.urbanspoon.com/r/6/92204", 
      { 
        "@type": "EntryPoint", 
        "urlTemplate": "http://api.urbanspoon.com/r/6/92204", 
        "contentType": "application/json+ld" 
      }, 
      "android-app://com.urbanspoon/http/www.urbanspoon.com/r/6/92204", 
      { 
        "@type": "EntryPoint", 
        "urlTemplate": "urbanspoon://r/6/92204", 
        "application": { 
          "@type": "SoftwareApplication", 
          "@id": "284708449", 
          "name": "Urbanspoon iPhone & iPad App", 
          "operatingSystem": "iOS"
        }
      },
      { 
        "@type": "EntryPoint", 
        "urlTemplate": "urbanspoon://r/6/92204", 
        "application": { 
          "@type": "SoftwareApplication", 
          "@id": "5b23b738-bb64-4829-9296-5bcb59bb0d2d", 
          "name": "Windows Phone App", 
          "operatingSystem": "Windows Phone 8" 
       } 
      } 
    ] 
  } 
} 

This gives us a mix of AS 2.0 and Schema.org syntax. The AS 2.0 objectType and displayName properties map cleanly and things Just Work.

Using purely Activity Streams 2.0 syntax, we can't exactly duplicate the same model but we can get close and express all the same information (and with a more concise JSON syntax).


{  
  "objectType": "http://schema.org/Restaurant", 
  "displayName": "Tartine Bakery", 
  "actions": {
    "view": [
      "http://www.urbanspoon.com/r/6/92204",
      "android-app://com.urbanspoon/http/www.urbanspoon.com/r/6/92204",
      { 
        "objectType": "HttpActionHandler", 
        "url": "http://api.urbanspoon.com/r/6/92204", 
        "mediaType": "application/json+ld" 
      },
      { 
        "objectType": "application", 
        "url": "urbanspoon://r/6/92204",
        "id": "284708449",
        "displayName": "Urbanspoon iPhone & iPad App",
        "operatingSystem": "iOS"
      },
       { 
        "objectType": "application", 
        "url": "urbanspoon://r/6/92204",
        "id": "5b23b738-bb64-4829-9296-5bcb59bb0d2d",
        "displayName": "Window Phone App",
        "operatingSystem": "Windows Phone 8"
      }
    ]
  },
  "@context": {
    "s": "http://schema.org/",
    "objectType": "@type",
    "displayName": "s:name",
    "potentialAction": "s:potentialAction",
    "actions": "s:potentialAction",
    "view": {
      "@type": "@vocab",
      "@id": "s:ViewAction"
    },
    "application": "s:SoftwareApplication",
    "HttpActionHandler": "s:EntryPoint",
    "template": "s:urlTemplate",
    "mediaType": "s:contentType",
    "url": "s:urlTemplate",
    "operatingSystem": "s:operatingSystem"
  }
} 

This yields the following N-Quads:


_:c14n0 <http://schema.org/name> "Urbanspoon iPhone & iPad App" .
_:c14n0 <http://schema.org/operatingSystem> "iOS" .
_:c14n0 <http://schema.org/urlTemplate> "urbanspoon://r/6/92204" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/SoftwareApplication> .
_:c14n1 <http://schema.org/name> "Tartine Bakery" .
_:c14n1 <http://schema.org/potentialAction> _:c14n4 .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Restaurant> .
_:c14n2 <http://schema.org/name> "Window Phone App" .
_:c14n2 <http://schema.org/operatingSystem> "Windows Phone 8" .
_:c14n2 <http://schema.org/urlTemplate> "urbanspoon://r/6/92204" .
_:c14n2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/SoftwareApplication> .
_:c14n3 <http://schema.org/contentType> "application/json+ld" .
_:c14n3 <http://schema.org/urlTemplate> "http://api.urbanspoon.com/r/6/92204" .
_:c14n3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/EntryPoint> .
_:c14n4 <http://schema.org/ViewAction> <android-app://com.urbanspoon/http/www.urbanspoon.com/r/6/92204> .
_:c14n4 <http://schema.org/ViewAction> <http://www.urbanspoon.com/r/6/92204> .
_:c14n4 <http://schema.org/ViewAction> _:c14n0 .
_:c14n4 <http://schema.org/ViewAction> _:c14n2 .
_:c14n4 <http://schema.org/ViewAction> _:c14n3 .

Breaking this down, this tells us that the "actions" property is equivalent to "http://schema.org/potentialAction", and that our "potentialAction" has five possible Actions. How these come across in the N-Quads is different relative to the pure schema.org alternative but it really is the same information.

One last example... The potential actions overview document includes the following:


{ 
 "@context": "http://schema.org", 
 "@type": "WebSite", 
 "name": "Example.com", 
 "potentialAction": { 
 "@type": "SearchAction", 
 "target": "http://example.com/search?q={q}", 
 "query-input": "required maxlength=100 name=q" 
 } 
} 

I have to say, that "query-input" element is just nasty. Here's the equivalent Activity Streams 2.0 Actions syntax (without the @context to keep things simple):


{
  "objectType": "http://schema.org/WebSite",
  "displayName": "Example.com",
  "actions": {
    "search": {
      "objectType": "UrlTemplate",
      "template": "http://example.com/search?q={q}",
      "parameters": {
        "q": {
          "required": true,
          "maxlength": 100
        }
      }
    }
  }
}

The former is less verbose but the latter will be easier to process given that it does not require any micro-syntax parsing.