Introducing my latest startup, Forage

I’ve always hated going to the grocery store. It’s such a pain to figure out what to make, how to get to the store and then find everything I need. I’m sick of throwing away food that I don’t eat in time. And why do I need to buy a $8 container of cumin when I only need 1 tsp!?!

I’m stoked to finally share what I’ve been working on for the last few months. Today we’re launching Forage — delicious meals that you can cook at home in 20 mins or less. All the ingredients are pre-measured so you can explore new types of dishes without all the waste.

I’d love to have you check it out. If nothing else, sign up to take advantage of some free food!

 2 comments

Inalienable rights

When I visited Hiroshima and the Peace museum there, I was blown away by the forgiveness exhibited by the Japanese people. Walking around that museum was one of the most moving experiences of my life. We called it the “cry museum”. There’s something wrong with you if you can walk through that museum without shedding a tear. The museum is a memorial for the atomic bomb victims and it shows the price of war in a very up-close and uncomfortable way.

Today I stumbled on this TED talk by George Takei on Why I love a country that once betrayed me:

Once again, I found myself blown away by the unbelievable amount of forgiveness by the Japanese people.

I was also reminded of how much I appreciate the ideals on which the United States was founded — the idea that all people are created equal. The idea that all people have an inalienable right to life, liberty and the pursuit of happiness.

Today there are countless examples of inequality in our country. It’s so easy to be discouraged. Takei’s story is certainly a solemn reminder of how easily we can slip away from these founding principles. Bono likes to talk about the “blind spots of our age”. We look back on the injustice we displayed to Japanese Americans with horror and disgrace. What are the things we’re accepting today that history will judge us for?

Those inalienable right are still worth defending.

  comments

The security hole I found on Amazon.com

I found a security hole on Amazon last August. While looking at their HTTP headers, I happened to notice that the entire amazon.com domain was susceptible to clickjacking attacks. If I could trick you into clicking anywhere on a webpage I controlled, I could get you to buy any product that’s available for sale on Amazon. By the way, that includes any fake products that I added to Amazon myself. For the hack to work, you needed to be signed into your Amazon account and have one-click purchasing turned on. I created a working proof-of-concept that looked like this:

amazon-clickjacking

Clicking either button caused an instant purchase of the movie Click (get it?). I resisted the temptation to use the exploit to send myself a million dollars worth of free Amazon gift cards, and instead responsibly disclosed it to the Amazon security team. It took them months to fix it, but the security hole has finally been closed using the x-frame-options header that I recommended.

This hack is classic clickjacking. I created a transparent iframe containing a product page on amazon.com that had been carefully positioned so when you think you’re clicking on my page, you’re actually clicking the “Buy now” button on their site instead. Here’s the code for the no longer working proof of concept.

 6 comments

Habits > Willpower

In the book The Willpower Instinct, Kelly McGonigal explains how your willpower is just like any other muscle. It gets tired. If you’re relying on your willpower to make the changes you want in your life, you’re likely going to fail. In the book Foodist, Darya Rose talks about the secret to eating better and the answer is not to go on another diet. It’s to build positive eating habits into your life. Positive habits beat willpower every time.

They say it takes 21 days to build a new habit.

It’s never to late to start building new habits into your life. I’m ashamed to say this, but it wasn’t until last year that I started flossing daily. Now it’s a daily routine for me. This month, I started doing the 7-min workout every morning. Now I roll out of bed and start doing jumping jacks. 7 minutes isn’t much, but that’s kinda the point. Overcoming inertia is the hard part. If you can’t find 7 minutes a day, it’s time you loosen up your schedule.

And there are plenty of tricks to help you get started. I found I was happiest when I biked to work instead of sitting in traffic and dealing with parking tickets. So I sold my car and treated my bike to a tune-up from the Bike Doctor (highly recommended service by the way).

I’m happier than I’ve been in a long time. For me the secret has been as simple as being intentional about finding what makes me happy and then turning those things into daily habits.

 1 comment

Taking a peek inside __VIEWSTATE

If you’ve ever viewed-source on a website that uses Microsoft technology like ASP or .NET, you may have noticed a massive blob of unintelligible text stored in a input field called __VIEWSTATE. What you’re seeing is actually a bunch of Base64 encoded data that gets passed back and forth between the server and the client. I don’t understand how anyone ever thought this was a good idea, but there are a ton of sites that still use this technique. Just check out some old enterprise applications or any Microsoft website and you’ll see what I mean. The United and US Airways websites are a couple other good examples. Unless the __VIEWSTATE is encrypted, you’ll be able to take a look inside using this simple bookmarklet:

javascript:xmp=document.createElement("xmp");
txt=document.createTextNode(atob(document.getElementsByName("__VIEWSTATE")[0].value));
xmp.appendChild(txt);
document.body.insertBefore(xmp,document.body.firstChild);

Drag this link to your toolbar to try it: Decode ViewState

  comments

Pinterest’s 25 edge cases

I was snooping around the JavaScript that Pinterest uses for their “Pin It” button. No real reason. I’m just nosy.

Most of the code is what you would expect, but what jumped out at me were the 25 websites that get special treatment. In fact, Pinterest’s JavaScript is riddled with special handling for edge-cases on third-party sites. As a developer, I have the tendency to always search for the universal solution – one piece of code that works everywhere. While universal code is arguably “better”, this more often than not is what code looks like in the “real world”.

lookup: {
        artsy: {
            page: {
                seek: [/^https?:\/\/(.*?\.|)artsy\.net\/artwork\//,
                    /^https?:\/\/(.*?\.|)artsy\.net\/post\//
                ]
            },
            img: {
                seek: [/^https?:\/\/(.*?\.|)artsy\.net\//],
                act: "lookup"
            }
        },
        behance: {
            img: {
                seek: [/^http:\/\/behance\.vo\.llnwd\.net\//],
                act: "lookup"
            }
        },
        dasauge: {
            img: {
                seek: [/^https?:\/\/cdn?[0-9]\.dasauge\.net\//],
                act: "lookup"
            }
        },
        dailymotion: {
            page: {
                seek: [/^https?:\/\/.*?\.dailymotion\.com\//],
                act: "lookup",
                via: "id",
                multimedia: true,
                doNotCrawl: true
            }
        },
        dreamstime: {
            img: {
                seek: [/(.*?)\.dreamstime\.com\//],
                act: "lookup"
            }
        },
        etsy: {
            page: {
                seek: [/^https?:\/\/.*?\.etsy\.com\/listing\//],
                patch: {
                    img: function (g) {
                        return g.replace(/il_(.*?)\./,
                            "il_570xN.")
                    }
                }
            },
            img: {
                seek: [/^https?:\/\/.*?\.etsystatic\.com\//],
                patch: function (g) {
                    return g.replace(/il_(.*?)\./, "il_570xN.")
                },
                act: "lookup"
            }
        },
        fivehundredpx: {
            page: {
                seek: [/^https?:\/\/500px\.com\/photo\//],
                act: "lookup",
                via: "id",
                doNotCrawl: true
            },
            img: {
                seek: [/pcdn\.500px\.net\//],
                act: "lookup"
            }
        },
        facebook: {
            page: {
                seek: [/^https?:\/\/(.*?\.|)facebook\.com\//],
                act: "close",
                msg: "privateDomain",
                patch: function (g) {
                    return g.replace(/%privateDomain%/, "Facebook")
                }
            }
        },
        flickr: {
            page: {
                seek: [/^https?:\/\/www\.flickr\.com\//],
                act: "lookup",
                via: "id",
                doNotCrawl: true
            },
            img: {
                seek: [/staticflickr.com\//, /static.flickr.com\//],
                act: "lookup"
            }
        },
        geograph: {
            img: {
                seek: [/^https?:\/\/(.*?)\.geograph\.org\./],
                act: "lookup"
            }
        },
        googleReader: {
            page: {
                seek: [/^https?:\/\/.*?\.google\.com\/reader\//],
                act: "close",
                msg: "privateDomain",
                patch: function (g) {
                    return g.replace(/%privateDomain%/, "Google Reader")
                }
            }
        },
        googleList: {
            page: {
                seek: [/^https?:\/\/www\.google\.com\/search(.*&tbm=isch.*)/],
                patch: function (g) {
                    g.f.debug("patching Google Image Search results");
                    var i, j, a, b, c, d;
                    if (i = g.d.getElementById("ires")) {
                        i = i.getElementsByTagName("A");
                        j = 0;
                        for (a = i.length; j < a; j += 1) {
                            d = c = "";
                            if (i[j].href) {
                                b = i[j].href.split("imgrefurl=");
                                if (b[1]) c = b[1].split("&")[0];
                                b = i[j].href.split("imgurl=");
                                if (b[1]) d = b[1].split("&")[0]
                            }
                            if (c && d) {
                                b = i[j].getElementsByTagName("IMG");
                                if (b[0]) {
                                    g.f.set(b[0], "data-pin-url", decodeURIComponent(c));
                                    g.f.set(b[0], "data-pin-media", decodeURIComponent(d))
                                }
                            }
                        }
                    }
                }
            }
        },
        imdb: {
            img: {
                seek: [/^https?:\/\/(.*?)\.media-imdb\.com\/images\//],
                patch: function (g) {
                    return g.replace(/@@(.*)/,
                        "@@._V1_SX800.jpg")
                }
            }
        },
        kickstarter: {
            page: {
                seek: [/^https?:\/\/.*?\.kickstarter\.com\/projects\//],
                act: "lookup",
                via: "id",
                multimedia: true
            }
        },
        pinterest: {
            page: {
                seek: [/^https?:\/\/(.*?\.|)pinterest\.com\//],
                act: "close",
                msg: "installed"
            }
        },
        polyvore: {
            page: {
                seek: [/^https?:\/\/(.*?\.|)polyvore\.com\//],
                act: "lookup",
                via: "id",
                doNotCrawl: true
            },
            img: {
                seek: [/^https?:\/\/(.*?)\.polyvoreimg\.com\//],
                act: "lookup"
            }
        },
        shutterstock: {
            img: {
                seek: [/^https?:\/\/image.shutterstock\.com\//, /^https?:\/\/thumb(.*?).shutterstock\.com\//],
                act: "lookup"
            }
        },
        slideshare: {
            page: {
                seek: [/^https?:\/\/.*?\.slideshare\.net\//],
                act: "lookup",
                via: "id",
                multimedia: true,
                doNotCrawl: true
            }
        },
        soundcloud: {
            page: {
                seek: [/^https?:\/\/soundcloud\.com\//],
                act: "lookup",
                via: "id",
                multimedia: true,
                doNotCrawl: true
            }
        },
        stumbleuponFrame: {
            page: {
                seek: [/^https?:\/\/(.*?\.|)stumbleupon\.com\/su/],
                act: "bustFrame",
                serviceName: "StumbleUpon",
                frameId: ["tb-stumble-frame", "stumbleFrame"]
            }
        },
        ted: {
            page: {
                seek: [/^https?:\/\/(.*?)\.ted\.com\/talks\//],
                act: "lookup",
                via: "id",
                multimedia: true,
                doNotCrawl: true
            },
            img: {
                seek: [/^https?:\/\/(.*?)\.ted\.com\//],
                act: "lookup"
            },
            iframe: {
                seek: [/^https?:\/\/(.*?)\.ted\.com\//],
                act: "lookup",
                via: "id"
            }
        },
        tumblr: {
            img: {
                seek: [/^https?:\/\/.*?\.media\.tumblr\.com\//],
                patch: function (g) {
                    return g.replace(/_(\d+)\.jpg$/, "_1280.jpg")
                }
            }
        },
        tumblrList: {
            page: {
                seek: [/^https?:\/\/www\.tumblr\.com\/tagged/, /^https?:\/\/www\.tumblr\.com\/dashboard/],
                patch: function (g) {
                    g.f.debug("patching Tumblr search or index");
                    var i, j, a, b, c, d, e, h;
                    i = g.d.getElementsByTagName("LI");
                    j = 0;
                    for (a =
                        i.length; j < a; j += 1) {
                        h = g.f.get(i[j], "data-tumblelog-content-rating");
                        b = i[j].getElementsByTagName("A");
                        e = "";
                        c = 0;
                        for (d = b.length; c < d; c += 1)
                            if (b[c].id && b[c].id.split("permalink_")[1]) {
                                e = b[c].href;
                                break
                            }
                        if (e) {
                            b = i[j].getElementsByTagName("IMG");
                            c = 0;
                            for (d = b.length; c < d; c += 1)
                                if (h === "adult") {
                                    g.f.set(b[c], "data-pin-nopin", true);
                                    g.f.debug("do not pin per Tumblr content rating: " + b[c].src);
                                    g.f.log("nsfw_per_domain", b[c].src, e)
                                } else g.f.set(b[c], "data-pin-url", e)
                        }
                    }
                }
            }
        },
        vimeo: {
            page: {
                seek: [/^https?:\/\/vimeo\.com\//],
                act: "lookup",
                via: "link",
                patch: function (g) {
                    g.f.debug("patching Vimeo page");
                    var i, j, a, b, c, d;
                    i = g.d.getElementsByTagName("LI");
                    j = 0;
                    for (a = i.length; j < a; j += 1) {
                        if (i[j].id && i[j].id.match(/^clip/)) {
                            b = i[j].id.split("clip");
                            if (b[1]) {
                                b[1] = b[1].replace(/_/, "");
                                g.f.thumbMedia("http://vimeo.com/" + b[1], "vimeo", "link")
                            }
                        } else {
                            b = i[j].getElementsByTagName("A");
                            if (b[0])(b = g.f.get(b[0], "data-id")) && g.f.thumbMedia("http://vimeo.com/" + b, "vimeo", "link")
                        }
                        b = i[j].getElementsByTagName("IMG");
                        c = 0;
                        for (d = b.length; c < d; c += 1) g.f.set(b[c],
                            "data-pin-nopin", true)
                    }
                }
            },
            iframe: {
                seek: [/^http?s:\/\/vimeo\.com\/(\d+)/, /^http:\/\/player\.vimeo\.com\/video\/(\d+)/],
                act: "lookup",
                via: "link",
                patch: function (g) {
                    var i = null;
                    g = g.split("#")[0].split("?")[0].split("/").pop();
                    if (g > 1E3) i = "http://vimeo.com/" + g;
                    return i
                },
                att: "src"
            }
        },
        youtube: {
            page: {
                seek: [/^https?:\/\/www\.youtube\.com\/watch/],
                act: "lookup",
                via: "link",
                multimedia: true,
                extended: true
            },
            video: {
                seek: [/^https?:\/\/(.*?\.|)youtube\.com\/videoplayback/],
                act: "lookup",
                via: "link",
                att: "data-youtube-id",
                patch: function (g) {
                    var i = null;
                    if (g) i = "http://www.youtube.com/embed/" + g;
                    return i
                }
            },
            iframe: {
                seek: [/^https?:\/\/(.*?\.|)youtube\.com\/embed\//],
                act: "lookup",
                via: "link"
            },
            embed: {
                seek: [/^http:\/\/s\.ytimg\.com\/yt\//],
                patch: function (g) {
                    var i = null;
                    g = g.split("video_id=");
                    if (g[1]) {
                        i = g[1].split("&")[0];
                        i = "http://www.youtube.com/embed/" + i
                    }
                    return i
                },
                att: "flashvars"
            },
            img: {
                seek: [/^https?:\/\/(.*?\.|)ytimg\.com\/(vi|li)\//, /img.youtube.com\/vi\//],
                act: "lookup"
            }
        }
    }
 2 comments

The big choices in life

A great quote from Steve Jobs:

Remembering that I’ll be dead soon is the most important tool I’ve ever encountered to help me make the big choices in life.

Almost everything–all external expectations, all pride, all fear of embarrassment or failure–these things just fall away in the face of death, leaving only what is truly important.

Remembering that you are going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked. There is no reason not to follow your heart.

 16 comments

Lest we forget…

The United States Bill of Rights is a remarkable document. Our founding fathers had incredible foresight about the need to limit the power of government and protect our rights of liberty. In recent years, many of these amendments have been under attack by our government. Some are being ignored altogether. The US Constitution and the principles behind it are a big part of what made us great. It made us a free country. We stood for something. These are our rights. We should read them more often and fight for them whenever necessary.

1. Congress shall make no law respecting an establishment of religion, or prohibiting the free exercise thereof; or abridging the freedom of speech, or of the press; or the right of the people peaceably to assemble, and to petition the Government for a redress of grievances.

2. A well regulated Militia, being necessary to the security of a free State, the right of the people to keep and bear Arms, shall not be infringed.

3. No Soldier shall, in time of peace be quartered in any house, without the consent of the Owner, nor in time of war, but in a manner to be prescribed by law.

4. The right of the people to be secure in their persons, houses, papers, and effects, against unreasonable searches and seizures, shall not be violated, and no Warrants shall issue, but upon probable cause, supported by Oath or affirmation, and particularly describing the place to be searched, and the persons or things to be seized.

5. No person shall be held to answer for a capital, or otherwise infamous crime, unless on a presentment or indictment of a Grand Jury, except in cases arising in the land or naval forces, or in the Militia, when in actual service in time of War or public danger; nor shall any person be subject for the same offence to be twice put in jeopardy of life or limb; nor shall be compelled in any criminal case to be a witness against himself, nor be deprived of life, liberty, or property, without due process of law; nor shall private property be taken for public use, without just compensation.

6. In all criminal prosecutions, the accused shall enjoy the right to a speedy and public trial, by an impartial jury of the State and district wherein the crime shall have been committed, which district shall have been previously ascertained by law, and to be informed of the nature and cause of the accusation; to be confronted with the witnesses against him; to have compulsory process for obtaining witnesses in his favor, and to have the Assistance of Counsel for his defence.

7. In suits at common law, where the value in controversy shall exceed twenty dollars, the right of trial by jury shall be preserved, and no fact tried by a jury, shall be otherwise re-examined in any court of the United States, than according to the rules of the common law.

8. Excessive bail shall not be required, nor excessive fines imposed, nor cruel and unusual punishments inflicted.

9. The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.

10. The powers not delegated to the United States by the Constitution, nor prohibited by it to the States, are reserved to the States respectively, or to the people.

People gave their lives so we could have these rights. How hard are we willing to fight to keep them?

  comments

Update on a few of my open source projects

I’ve been trying to do some cleanup and reorganization of my various open source projects. I regularly get emails asking for help or gently reminding me that one of my projects needs updating. The truth is, most of my projects have been horribly neglected for the last couple years. While I can’t promise my participation will improve, I would like to recognize some of the extraordinary people who have been helping me with maintenance while I’ve been busy. Here’s a quick status update on five of my more popular open source projects.

PubSubHubbub plugin for WordPress

I know, I know, RSS is supposed to be dead. Tell that to the 1,300+ people who downloaded my PubSubHubbub (PuSH) plugin last week, or the 58,000+ people who’ve downloaded it since it was first released. A huge thanks to Matthias Pfefferle for his help maintaining the project. It’s hard to say what’s going to happen to RSS & PuSH, especially as Google kills their Reader product. I expect big companies to continue to push for more centralized systems. Meanwhile, I’ll be here rooting for the open and distributed web however I can.

Timezone detection in JavaScript

My timezone detection article continues to be one of my more popular posts on this blog. Today there are thousands of websites using some variation of my timezone detection code. Jon Nylander took my code and has expanded it into a far more robust solution. His version is way better than mine, so use it instead.

Backwards compatible window.postMessage

My cross-browser implementation of window.postMessage is now pretty stable and is still one of the simplest solutions I’ve seen. I’ve heard that Twitter and Disqus both use easyXDM instead, but I haven’t dug into it myself to know how it compares.

Name Parser

Splitting names is a surprisingly tricky task. My solution isn’t perfect, but it’s a nice step beyond just splitting a name based on a space. Mark Pemburn did a nice job porting my library from PHP to JavaScript. This library has a lot of potential for improvement and I’d love to see more contributions here.

Rolling Curl

Another popular post (especially with the rent-a-coder crowd trying to scrape websites) was my Rolling Curl library in PHP. It solves the blocking connection problem with using cURL in PHP. Alexander Makarow and Fabian Franz have done a great job at improving my code and adding new features.

I’ve found it incredibly rewarding to participate in these projects and others. It blows my mind how much of the world runs on open source software. Open source was one of those things that never fully made sense to me until I started contributing myself. I encourage every developer to find a way to get involved in an open source project and give back. It’s been a great way for me to meet other passionate developers and learn from people who are smarter than I.

  comments

Learn to code

The team at code.org published a terrific video encouraging people to learn to code. It features some of the biggest names in our industry and it does a great job at explaining why programming skills are so important in this day and age. They want every student in every school to have the opportunity to learn to code. I’ve shared before the story of how I became a programmer. I was privileged to have access to a computer at a young age and a dad who encouraged me to learn to code. Considering the ever increasing demand for programming skills, it’s crazy that nine out of ten U.S. schools don’t offer computer programming classes at all. This is a great initiative to raise awareness and help inspire the next generation of developers.

 6 comments