Skip to main content

How shields.io uses the GitHub API

· 2 min read
chris48s
Shields.io Core Team

We serve a lot of badges which display information fetched from the GitHub API. When I say a lot, this varies a bit but in a typical hour we make hundreds of thousands of calls to the GitHub API.

But hang on. GitHub's API has rate limits.

Specifically, users can make up to 5,000 requests per hour to GitHub's v3/REST API. The v4/GraphQL also applies rate limits, but it is based on a slightly more complicated points-based system.

In any case, we are clearly making many times more requests to GitHub's API than would be allowed with a single token.

So how are we doing that? Well, we have lots of tokens. To elaborate on that slightly, as a user of shields.io you can choose to share a token with us to help increase our rate limit. Here's how it works:

  • Authorize our OAuth Application.
  • This shares with us a GitHub token which has read-only access to public data. We only ask for the minimum permissions necessary. Authorizing the OAuth app doesn't allow us access to your private data or allow us to perform any actions on your behalf.
  • Your token is added to a pool of tokens shared by other users like you.
  • When we need to make a request to the GitHub API, we pick one of the tokens from our pool. We only make a handful of requests with each token before picking another from the pool.
  • If you ever decide you would not like to continue sharing a token with us, you can revoke the Shields.io OAuth app at https://github.com/settings/applications. You can do this at any time. This will de-activate the token you have shared with us and we'll remove it from the pool.

This method allows us (with your help) to make hundreds of thousands of request per hour to the GitHub API. Because we have thousands of tokens in the pool and we only make a few requests with each one before picking another token from the pool, most users don't notice any meaningful impact on their available rate limit as a result of authorizing our app.

Our response to RCE Security Advisory

· 12 min read
chris48s
Shields.io Core Team

We've just published a critical security advisory relating to a Remote Code Execution vulnerability in Dynamic JSON/TOML/YAML badges: https://github.com/badges/shields/security/advisories/GHSA-rxvx-x284-4445 Thanks to @nickcopi for his help with this.

If you self-host your own instance of Shields you should upgrade to server-2024-09-25 or later as soon as possible to protect your instance.

This is primarily a concern for self-hosting users. However this does also have a couple of knock-on implications for some users of shields.io itself.

1. Users who have authorized the Shields.io GitHub OAuth app

While we have taken steps to close this vulnerability quickly after becoming aware of it, this attack vector has existed in our application for some time. We aren't aware of it having been actively exploited on shields.io. We also can't prove that it has not been exploited.

We don't log or track our users, so a breach offers a very limited attack surface against end users of shields.io. This is by design. One of the (few) information assets shields.io does hold is our GitHub token pool. This allows users to share a token with us by authorizing our OAuth app. Doing this gives us access to a token with read-only access to public data which we can use to increase our rate limit when making calls to the GitHub API.

The tokens we hold are not of high value to an attacker because they have read-only access to public data, but we can't say for sure they haven't been exfiltrated. If you've donated a token in the past and want to revoke it, you can revoke the Shields.io OAuth app at https://github.com/settings/applications which will de-activate the token you have shared with us.

2. Users of Dynamic JSON/TOML/YAML badges

Up until now, we have been using https://github.com/dchester/jsonpath as our library querying documents using JSONPath expressions. @nickcopi responsibly reported to us how a prototype pollution vulnerability in this library could be exploited to construct a JSONPath expression allowing an attacker to perform remote code execution. This vulnerability was reported on the package's issue tracker but not flagged by security scanning tools. It seems extremely unlikely that this will be fixed in the upstream package despite being widely used. It also seems unlikely this package will receive any further maintenance in future, even in response to critical security issues. In order to resolve this issue, we needed to switch to a different JSONPath library. We've decided to switch https://github.com/JSONPath-Plus/JSONPath using the eval: false option to disable script expressions.

This is an important security improvement and we have to make a change to protect the security of shields.io and users hosting their own instance of the application. However, this does come with some tradeoffs from a backwards-compatibility perspective.

Using eval: false

Using JSONPath-Plus with eval: false does disable some query syntax which relies on evaluating javascript expressions.

For example, it would previously have been possible to use a JSONPath query like $..keywords[(@.length-1)] against the document https://github.com/badges/shields/raw/master/package.json to select the last element from the keywords array https://github.com/badges/shields/blob/e237e40ab88b8ad4808caad4f3dae653822aa79a/package.json#L6-L12

This is now not a supported query.

In this particular case, you could rewrite that query to $..keywords[-1:] and obtain the same result, but that may not be possible in all cases. We do realise that this removes some functionality that previously worked but closing this remote code execution vulnerability is the top priority, especially since there will be workarounds in many cases.

Implementation Quirks

Historically, every JSONPath implementation has had its own idiosyncrasies. While most simple and common queries will behave the same way across different implementations, switching to another library will mean that some subset of queries may not work or produce different results.

One interesting thing that has happened in the world of JSONPath lately is RFC 9535 https://www.rfc-editor.org/rfc/rfc9535 which is an attempt to standardise JSONPath. As part of this mitigation, we did look at whether it would be possible to migrate to something that is RFC9535-compliant. However it is our assessment that the JavaScript community does not yet have a sufficiently mature/supported RFC9535-compliant JSONPath implementation. This means we are switching from one quirky implementation to another implementation with different quirks.

Again, this represents an unfortunate break in backwards-compatibility. However, it was necessary to prioritise closing off this remote code execution vulnerability over backwards-compatibility.

Although we can not provide a precise migration guide, here is a table of query types where https://github.com/dchester/jsonpath and https://github.com/JSONPath-Plus/JSONPath are known to diverge from the consensus implementation. This is sourced from the excellent https://cburgmer.github.io/json-path-comparison/ While this is a long list, many of these inputs represent edge cases or pathological inputs rather than common usage.

Table
Query TypeExample Query
Array slice with large number for end and negative step$[2:-113667776004:-1]
Array slice with large number for start end negative step$[113667776004:2:-1]
Array slice with negative step$[3:0:-2]
Array slice with negative step on partially overlapping array$[7:3:-1]
Array slice with negative step only$[::-2]
Array slice with open end and negative step$[3::-1]
Array slice with open start and negative step$[:2:-1]
Array slice with range of 0$[0:0]
Array slice with step 0$[0:3:0]
Array slice with step and leading zeros$[010:024:010]
Bracket notation with empty path$[]
Bracket notation with number on object$[0]
Bracket notation with number on string$[0]
Bracket notation with number -1$[-1]
Bracket notation with quoted array slice literal$[':']
Bracket notation with quoted closing bracket literal$[']']
Bracket notation with quoted current object literal$['@']
Bracket notation with quoted escaped backslash$['\']
Bracket notation with quoted escaped single quote$[''']
Bracket notation with quoted root literal$['$']
Bracket notation with quoted special characters combined$[':@."$,*'\']
Bracket notation with quoted string and unescaped single quote$['single'quote']
Bracket notation with quoted union literal$[',']
Bracket notation with quoted wildcard literal ?$['*']
Bracket notation with quoted wildcard literal on object without key$['*']
Bracket notation with spaces$[ 'a' ]
Bracket notation with two literals separated by dot$['two'.'some']
Bracket notation with two literals separated by dot without quotes$[two.some]
Bracket notation without quotes$[key]
Current with dot notation@.a
Dot bracket notation$.['key']
Dot bracket notation with double quotes$.["key"]
Dot bracket notation without quotes$.[key]
Dot notation after recursive descent with extra dot ?$...key
Dot notation after union with keys$['one','three'].key
Dot notation with dash$.key-dash
Dot notation with double quotes$."key"
Dot notation with double quotes after recursive descent ?$.."key"
Dot notation with empty path$.
Dot notation with key named length on array$.length
Dot notation with key root literal$.$
Dot notation with non ASCII key$.??
Dot notation with number$.2
Dot notation with number -1$.-1
Dot notation with single quotes$.'key'
Dot notation with single quotes after recursive descent ?$..'key'
Dot notation with single quotes and dot$.'some.key'
Dot notation with space padded key$. a
Dot notation with wildcard after recursive descent on scalar ?$..*
Dot notation without dot$a
Dot notation without root.key
Dot notation without root and dotkey
Emptyn/a
Filter expression on object$[?(@.key)]
Filter expression after dot notation with wildcard after recursive descent ?$..*[?(@.id>2)]
Filter expression after recursive descent ?$..[?(@.id==2)]
Filter expression with addition$[?(@.key+50==100)]
Filter expression with boolean and operator and value false$[?(@.key>0 && false)]
Filter expression with boolean and operator and value true$[?(@.key>0 && true)]
Filter expression with boolean or operator and value false$[?(@.key>0 || false)]
Filter expression with boolean or operator and value true$[?(@.key>0 || true)]
Filter expression with bracket notation with -1$[?(@[-1]==2)]
Filter expression with bracket notation with number on object$[?(@[1]=='b')]
Filter expression with current object$[?(@)]
Filter expression with different ungrouped operators$[?(@.a && @.b || @.c)]
Filter expression with division$[?(@.key/10==5)]
Filter expression with dot notation with dash$[?(@.key-dash == 'value')]
Filter expression with dot notation with number$[?(@.2 == 'second')]
Filter expression with dot notation with number on array$[?(@.2 == 'third')]
Filter expression with empty expression$[?()]
Filter expression with equals$[?(@.key==42)]
Filter expression with equals on array of numbers$[?(@==42)]
Filter expression with equals on object$[?(@.key==42)]
Filter expression with equals array$[?(@.d==["v1","v2"])]
Filter expression with equals array for array slice with range 1$[?(@[0:1]==[1])]
Filter expression with equals array for dot notation with star$[?(@.*==[1,2])]
Filter expression with equals array or equals true$[?(@.d==["v1","v2"] || (@.d == true))]
Filter expression with equals array with single quotes$[?(@.d==['v1','v2'])]
Filter expression with equals boolean expression value$[?((@.key<44)==false)]
Filter expression with equals false$[?(@.key==false)]
Filter expression with equals null$[?(@.key==null)]
Filter expression with equals number for array slice with range 1$[?(@[0:1]==1)]
Filter expression with equals number for bracket notation with star$[?(@[*]==2)]
Filter expression with equals number for dot notation with star$[?(@.*==2)]
Filter expression with equals number with fraction$[?(@.key==-0.123e2)]
Filter expression with equals number with leading zeros$[?(@.key==010)]
Filter expression with equals object$[?(@.d=={"k":"v"})]
Filter expression with equals string$[?(@.key=="value")]
Filter expression with equals string with unicode character escape$[?(@.key=="Mot\u00f6rhead")]
Filter expression with equals true$[?(@.key==true)]
Filter expression with equals with path and path$[?(@.key1==@.key2)]
Filter expression with equals with root reference$.items[?(@.key==$.value)]
Filter expression with greater than$[?(@.key>42)]
Filter expression with greater than or equal$[?(@.key>=42)]
Filter expression with in array of values$[?(@.d in [2, 3])]
Filter expression with in current object$[?(2 in @.d)]
Filter expression with length free function$[?(length(@) == 4)]
Filter expression with length function$[?(@.length() == 4)]
Filter expression with length property$[?(@.length == 4)]
Filter expression with less than$[?(@.key<42)]
Filter expression with less than or equal$[?(@.key<=42)]
Filter expression with local dot key and null in data$[?(@.key='value')]
Filter expression with multiplication$[?(@.key*2==100)]
Filter expression with negation and equals$[?(!(@.key==42))]
Filter expression with negation and equals array or equals true$[?(!(@.d==["v1","v2"]) &#124;&#124; (@.d == true))]
Filter expression with negation and less than$[?(!(@.key<42))]
Filter expression with negation and without value$[?(!@.key)]
Filter expression with non singular existence test$[?(@.a.*)]
Filter expression with not equals$[?(@.key!=42)]
Filter expression with not equals array or equals true$[?((@.d!=["v1","v2"]) &#124;&#124; (@.d == true))]
Filter expression with parent axis operator$[*].bookmarks[?(@.page == 45)]^^^
Filter expression with regular expression$[?(@.name=~/hello.*/)]
Filter expression with regular expression from member$[?(@.name=~/@.pattern/)]
Filter expression with set wise comparison to scalar$[?(@[*]>=4)]
Filter expression with set wise comparison to set$.x[?(@[]>=$.y[])]
Filter expression with single equal$[?(@.key=42)]
Filter expression with subfilter$[?(@.a[?(@.price>10)])]
Filter expression with subpaths deeply nested$[?(@.a.b.c==3)]
Filter expression with subtraction$[?(@.key-50==-100)]
Filter expression with triple equal$[?(@.key===42)]
Filter expression with value$[?(@.key)]
Filter expression with value after recursive descent ?$..[?(@.id)]
Filter expression with value false$[?(false)]
Filter expression with value from recursive descent$[?(@..child)]
Filter expression with value null$[?(null)]
Filter expression with value true$[?(true)]
Filter expression without parens$[?@.key==42]
Filter expression without value$[?(@.key)]
Function sum$.data.sum()
Parens notation$(key,more)
Recursive descent ?$..
Recursive descent after dot notation ?$.key..
Root on scalar$
Root on scalar false$
Root on scalar true$
Script expression$[(@.length-1)]
Union with duplication from array$[0,0]
Union with duplication from object$['a','a']
Union with filter$[?(@.key<3),?(@.key>6)]
Union with keys$['key','another']
Union with keys on object without key$['missing','key']
Union with keys after array slice$[:]['c','d']
Union with keys after bracket notation$[0]['c','d']
Union with keys after dot notation with wildcard$.*['c','d']
Union with keys after recursive descent ?$..['c','d']
Union with repeated matches after dot notation with wildcard$.*[0,:5]
Union with slice and number$[1:3,4]
Union with spaces$[ 0 , 1 ]
Union with wildcard and number$[*,1]

Sunsetting Shields custom logos

· 2 min read
PyvesB
Shields.io Core Team

Following discussions in #9476, we've gone ahead and deleted all custom logos that were maintained on the Shields.io side (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis), and will solely rely on the Simple-Icons project to provide named logos for our badges from now on. If you were using a Shields custom logo, you will have transparently switched over to the corresponding Simple-Icon and do not need to make changes to your badges.

The reasons behind this decision include the following:

  • reducing code complexity and induced overhead by deleting several dozens lines of code.
  • reducing maintenance load; we received regular pull requests to add logos that do not comply with our guidelines, or various other related questions.
  • providing a less confusing user experience; all named logos now behave in the same way with regards to logoColor and other parameters.
  • reducing frustration for contributors who prepared logo pull requests only to be told that they hadn't read the guidelines or that there was a misalignment on the interpretation of said guidelines.
  • reinforcing Shields.io's mission to provide consistent badges, with all named logos now being monochrome.
  • improving compliance with third-party brands; Simple-Icons regularly reviews whether their icons respect latest brand guidelines, whereas we do not.
  • unblocking #4947.

We do acknowledge the fact that some of you voiced your preference for a given Shields custom logo over its Simple-Icons equivalent in #7684. If you really want to go back to the Shields custom logo, you can leverage custom logos to do so. Here are the corresponding Base64-encoded logo parameters for all our existing logos:

NameLogo Previewlogo Parameter
bitcoinbitcoindata:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIzLjYzNiAxNC45MDJjLTEuNjAyIDYuNDMtOC4xMTQgMTAuMzQyLTE0LjU0MyA4Ljc0QzIuNjY2IDIyLjAzNy0xLjI0NiAxNS41MjUuMzU3IDkuMDk4IDEuOTYgMi42NjkgOC40Ny0xLjI0NCAxNC44OTcuMzU5YzYuNDMgMS42MDIgMTAuMzQxIDguMTE1IDguNzM5IDE0LjU0NCIgZmlsbD0iI2Y3OTMxYSIvPjxwYXRoIGQ9Ik0xNC42ODYgMTAuMjY3Yy0uMzcxIDEuNDg3LTIuNjYzLjczMS0zLjQwNi41NDZsLjY1NS0yLjYyOWMuNzQzLjE4NiAzLjEzOC41MzEgMi43NSAyLjA4M20tLjQwNiA0LjI0MmMtLjQwNyAxLjYzNS0zLjE2Ljc1LTQuMDUzLjUzbC43MjQtMi45Yy44OTMuMjI0IDMuNzU0LjY2NCAzLjMzIDIuMzdtMy4wMDgtNC4yMTljLjIzOC0xLjU5Ni0uOTc3LTIuNDU1LTIuNjQtMy4wMjdsLjU0LTIuMTYzLTEuMzE4LS4zMy0uNTI1IDIuMTA3YTU0LjI5MiA1NC4yOTIgMCAwIDAtMS4wNTQtLjI0OWwuNTMtMi4xMi0xLjMxNy0uMzI4LS41NCAyLjE2MmMtLjI4Ni0uMDY1LS41NjctLjEzLS44NC0uMTk4bC4wMDEtLjAwNy0xLjgxNi0uNDUzLS4zNSAxLjQwNnMuOTc3LjIyNC45NTYuMjM4Yy41MzMuMTMzLjYzLjQ4Ni42MTMuNzY2bC0uNjE1IDIuNDYzYy4wMzguMDEuMDg1LjAyNC4xMzcuMDQ1bC0uMTM4LS4wMzUtLjg2MiAzLjQ1MmMtLjA2NS4xNjEtLjIzLjQwNS0uNjA0LjMxMi4wMTQuMDItLjk1Ny0uMjM5LS45NTctLjIzOUw1LjgzNiAxNS42bDEuNzE0LjQyN2MuMzE4LjA4LjYzLjE2NC45MzguMjQybC0uNTQ1IDIuMTkgMS4zMTUuMzI4LjU0LTIuMTY0Yy4zNi4wOTcuNzA4LjE4NyAxLjA1LjI3MWwtLjUzOCAyLjE1NiAxLjMxNi4zMjguNTQ2LTIuMTgzYzIuMjQ1LjQyNCAzLjkzMy4yNTMgNC42NDMtMS43NzcuNTc0LTEuNjM1LS4wMjctMi41NzgtMS4yMDgtMy4xOTQuODYtLjE5OCAxLjUwOC0uNzY1IDEuNjgxLTEuOTM0IiBmaWxsPSIjZmZmIi8+PC9zdmc+
dependabotdependabotdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NCA1NCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTI1IDNhMSAxIDAgMCAwLTEgMXY3YTEgMSAwIDAgMCAxIDFoNXYzSDZhMyAzIDAgMCAwLTMgM3YxMkgxYTEgMSAwIDAgMC0xIDF2MTBhMSAxIDAgMCAwIDEgMWgydjZhMyAzIDAgMCAwIDMgM2g0MmEzIDMgMCAwIDAgMy0zdi02aDJhMSAxIDAgMCAwIDEtMVYzMWExIDEgMCAwIDAtMS0xaC0yVjE4YTMgMyAwIDAgMC0zLTNIMzNWNGExIDEgMCAwIDAtMS0xaC03em0tMy45ODIgMjZhMS4yMSAxLjIxIDAgMCAxIC44MzcuMzU1bDEuMjkgMS4yOWExLjIxIDEuMjEgMCAwIDEgMCAxLjcwOSAxLjIxIDEuMjEgMCAwIDEgMCAuMDAxbC02LjI5MSA2LjI5YTEuMjEgMS4yMSAwIDAgMS0xLjcxIDBsLTMuNzktMy43OTFhMS4yMSAxLjIxIDAgMCAxIDAtMS43MWwxLjI5LTEuMjlhMS4yMSAxLjIxIDAgMCAxIDEuNzEgMEwxNiAzMy41bDQuMTQ1LTQuMTQ1YTEuMjEgMS4yMSAwIDAgMSAuODczLS4zNTV6bTE5Ljk2MiAwYTEuMjEgMS4yMSAwIDAgMSAuODc0LjM1NGwxLjI5IDEuMjlhMS4yMSAxLjIxIDAgMCAxIDAgMS43MWwtNi4yOSA2LjI4OXYuMDAyYTEuMjEgMS4yMSAwIDAgMS0xLjcxMSAwbC0zLjc5LTMuNzlhMS4yMSAxLjIxIDAgMCAxIDAtMS43MWwxLjI5LTEuMjlhMS4yMSAxLjIxIDAgMCAxIDEuNzEgMGwxLjY0NSAxLjY0NSA0LjE0Ny00LjE0NkExLjIxIDEuMjEgMCAwIDEgNDAuOTggMjl6Ii8+PC9zdmc+
gitlabgitlabdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjkzIDkzIDE5NCAxOTQiPjxkZWZzPjxzdHlsZT4uYntmaWxsOiNmYzZkMjZ9PC9zdHlsZT48L2RlZnM+PHBhdGggc3R5bGU9ImZpbGw6I2UyNDMyOSIgZD0ibTI4Mi44MyAxNzAuNzMtLjI3LS42OS0yNi4xNC02OC4yMmE2LjgxIDYuODEgMCAwIDAtMi42OS0zLjI0IDcgNyAwIDAgMC04IC40MyA3IDcgMCAwIDAtMi4zMiAzLjUybC0xNy42NSA1NGgtNzEuNDdsLTE3LjY1LTU0YTYuODYgNi44NiAwIDAgMC0yLjMyLTMuNTMgNyA3IDAgMCAwLTgtLjQzIDYuODcgNi44NyAwIDAgMC0yLjY5IDMuMjRMOTcuNDQgMTcwbC0uMjYuNjlhNDguNTQgNDguNTQgMCAwIDAgMTYuMSA1Ni4xbC4wOS4wNy4yNC4xNyAzOS44MiAyOS44MiAxOS43IDE0LjkxIDEyIDkuMDZhOC4wNyA4LjA3IDAgMCAwIDkuNzYgMGwxMi05LjA2IDE5LjctMTQuOTEgNDAuMDYtMzAgLjEtLjA4YTQ4LjU2IDQ4LjU2IDAgMCAwIDE2LjA4LTU2LjA0WiIvPjxwYXRoIGNsYXNzPSJiIiBkPSJtMjgyLjgzIDE3MC43My0uMjctLjY5YTg4LjMgODguMyAwIDAgMC0zNS4xNSAxNS44TDE5MCAyMjkuMjVjMTkuNTUgMTQuNzkgMzYuNTcgMjcuNjQgMzYuNTcgMjcuNjRsNDAuMDYtMzAgLjEtLjA4YTQ4LjU2IDQ4LjU2IDAgMCAwIDE2LjEtNTYuMDhaIi8+PHBhdGggc3R5bGU9ImZpbGw6I2ZjYTMyNiIgZD0ibTE1My40MyAyNTYuODkgMTkuNyAxNC45MSAxMiA5LjA2YTguMDcgOC4wNyAwIDAgMCA5Ljc2IDBsMTItOS4wNiAxOS43LTE0LjkxUzIwOS41NSAyNDQgMTkwIDIyOS4yNWMtMTkuNTUgMTQuNzUtMzYuNTcgMjcuNjQtMzYuNTcgMjcuNjRaIi8+PHBhdGggY2xhc3M9ImIiIGQ9Ik0xMzIuNTggMTg1Ljg0QTg4LjE5IDg4LjE5IDAgMCAwIDk3LjQ0IDE3MGwtLjI2LjY5YTQ4LjU0IDQ4LjU0IDAgMCAwIDE2LjEgNTYuMWwuMDkuMDcuMjQuMTcgMzkuODIgMjkuODJMMTkwIDIyOS4yMVoiLz48L3N2Zz4=
npmnpmdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZD0iTTAgMGg0MHY0MEgwVjB6IiBmaWxsPSIjY2IwMDAwIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTcgN2gyNnYyNmgtN1YxNGgtNnYxOUg3eiIvPjwvc3ZnPgo=
paypalpaypaldata:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE5LjcxNSA2LjEzM2MuMjQ5LTEuODY2IDAtMy4xMS0uOTk5LTQuMjY2QzE3LjYzNC42MjIgMTUuNzIxIDAgMTMuMzA3IDBINi4yMzVjLS40MTggMC0uOTE2LjQ0NC0xIC44ODlMMi4zMjMgMjAuNjIyYzAgLjM1Ni4yNS44LjY2NS44aDQuMzI4bC0uMjUgMS45NTZjLS4wODQuMzU1LjE2Ni42MjIuNDk4LjYyMmgzLjY2M2MuNDE3IDAgLjgzMi0uMjY3LjkxNS0uNzExdi0uMjY3bC43NDktNC42MjJ2LS4xNzhjLjA4My0uNDQ0LjUtLjguOTE1LS44aC41YzMuNTc4IDAgNi4zMjUtMS41MSA3LjE1Ni01Ljk1NS40MTgtMS44NjcuMjUyLTMuMzc4LS43NDctNC40NDUtLjI1LS4zNTUtLjY2Ni0uNjIyLTEtLjg4OSIgZmlsbD0iIzAwOWNkZSIvPjxwYXRoIGQ9Ik0xOS43MTUgNi4xMzNjLjI0OS0xLjg2NiAwLTMuMTEtLjk5OS00LjI2NkMxNy42MzQuNjIyIDE1LjcyMSAwIDEzLjMwNyAwSDYuMjM1Yy0uNDE4IDAtLjkxNi40NDQtMSAuODg5TDIuMzIzIDIwLjYyMmMwIC4zNTYuMjUuOC42NjUuOGg0LjMyOGwxLjE2NC03LjM3OC0uMDgzLjI2N2MuMDg0LS41MzMuNS0uODg5Ljk5OC0uODg5aDIuMDhjNC4wNzkgMCA3LjI0MS0xLjc3OCA4LjI0LTYuNzU1LS4wODMtLjI2NyAwLS4zNTYgMC0uNTM0IiBmaWxsPSIjMDEyMTY5Ii8+PHBhdGggZD0iTTkuNTYzIDYuMTMzYy4wODItLjI2Ni4yNS0uNTMzLjQ5OC0uNzEuMTY2IDAgLjI1LS4wOS40MTYtLjA5aDUuNDk0Yy42NjYgMCAxLjMzLjA5IDEuODMuMTc4LjE2NiAwIC4zMzMgMCAuNDk4LjA4OS4xNjguMDg5LjMzNC4wODkuNDE4LjE3OGguMjVjLjI0OC4wODkuNDk3LjI2Ni43NDguMzU1LjI0OC0xLjg2NiAwLTMuMTEtLjk5OS00LjM1NUMxNy43MTcuNTMzIDE1LjgwNCAwIDEzLjM5IDBINi4yMzVjLS40MTggMC0uOTE2LjM1Ni0xIC44ODlMMi4zMjMgMjAuNjIyYzAgLjM1Ni4yNS44LjY2NS44aDQuMzI4bDEuMTY0LTcuMzc4IDEuMDg0LTcuOTF6IiBmaWxsPSIjMDAzMDg3Ii8+PC9zdmc+
serverfaultserverfaultdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAgMTIwIj48c3R5bGU+LnN0MHtmaWxsOiNhN2E5YWN9LnN0MXtmaWxsOiM4MTgyODV9LnN0MntmaWxsOiM1ODU4NWF9LnN0M3tmaWxsOiNkMWQyZDR9LnN0NHtmaWxsOiMyMzFmMjB9PC9zdHlsZT48cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTMuNyA0MS42aDQ0djguN2gtNDR6Ii8+PHBhdGggY2xhc3M9InN0MSIgZD0iTTEzLjcgNTUuOGg0NHY4LjdoLTQ0eiIvPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMy43IDY5aDQ0djguN2gtNDR6Ii8+PHBhdGggY2xhc3M9InN0MyIgZD0iTTEzLjcgMjcuNmg0NHY4LjdoLTQ0eiIvPjxwYXRoIGNsYXNzPSJzdDQiIGQ9Ik0xMy43IDgzLjJoNDR2OC43aC00NHoiLz48cGF0aCBmaWxsPSIjOTkyMjI0IiBkPSJNNjMgNDEuNmgxOC43djguN0g2M3oiLz48cGF0aCBmaWxsPSIjNjMwZjE2IiBkPSJNNjMgNTUuOGgxOC43djguN0g2M3oiLz48cGF0aCBmaWxsPSIjMmIxNDE1IiBkPSJNNjMgNjloMTguN3Y4LjdINjN6Ii8+PHBhdGggZmlsbD0iI2U3MjgyZCIgZD0iTTYzIDI3LjZoMTguN3Y4LjdINjN6Ii8+PHBhdGggY2xhc3M9InN0NCIgZD0iTTYzIDgzLjJoMTguN3Y4LjdINjN6Ii8+PGc+PHBhdGggY2xhc3M9InN0MCIgZD0iTTg2LjggNDJoMTguN3Y4LjdIODYuOHoiLz48cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOCA1Ni4yaDE4Ljd2OC43SDg2Ljh6Ii8+PHBhdGggY2xhc3M9InN0MiIgZD0iTTg2LjggNjkuNGgxOC43djguN0g4Ni44eiIvPjxwYXRoIGNsYXNzPSJzdDMiIGQ9Ik04Ni44IDI4aDE4Ljd2OC43SDg2Ljh6Ii8+PHBhdGggY2xhc3M9InN0NCIgZD0iTTg2LjggODMuNmgxOC43djguN0g4Ni44eiIvPjwvZz48L3N2Zz4=
stackexchangestackexchangedata:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuMjczIDEwLjQ2M2gxOS4zMjV2My45NzhIMi4yNzN6IiBmaWxsPSIjMzc2ZGI2Ii8+PHBhdGggZD0iTTIuMjczIDUuMzIyaDE5LjMyNVY5LjNIMi4yNzN6IiBmaWxsPSIjNGNhMmRhIi8+PHBhdGggZD0iTTE4LjU3NSAwSDUuMzc0Yy0xLjcwNSAwLTMuMSAxLjQyLTMuMSAzLjE3OFY0LjIxaDE5LjMyNFYzLjE3OEMyMS41OTggMS40MiAyMC4yNTQgMCAxOC41NzUgMHoiIGZpbGw9IiM5MWQ4ZjQiLz48cGF0aCBkPSJNMi4yNzMgMTUuNTc4djEuMDMzYzAgMS43NTcgMS4zOTYgMy4xNzggMy4xIDMuMTc4aDguMjY4VjI0bDQuMDgxLTQuMjExaC45MDVjMS43MDUgMCAzLjEtMS40MiAzLjEtMy4xNzh2LTEuMDMzeiIgZmlsbD0iIzFlNTM5NyIvPjwvc3ZnPg==
superusersuperuserdata:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNTk0IDBhLjUxNC41MTQgMCAwIDAtLjM0NC4xMS40MDQuNDA0IDAgMCAwLS4xMzMuMzA2djIzLjE5N2MwIC4xMjQuMDQ4LjI0Ni4xNDUuMzEyLjA5Ni4wNjUuMjA4LjA3NS4zMzIuMDc1aDUuNTkzYy4xMyAwIC4yNDMtLjAyLjMzNC0uMDkzLjA5Mi0uMDcyLjEzMS0uMi4xMTItLjMxN2wuMDAyLjAyM3YtMS40NjdhLjM2Ny4zNjcgMCAwIDAtLjE2LS4zMDEuNjEyLjYxMiAwIDAgMC0uMzQ0LS4wODdINS42MTNjLS4xMSAwLS4xNy0uMDItLjE5MS0uMDM3LS4wMjItLjAxNi0uMDMyLS4wMy0uMDMyLS4xVjIuNDA4YzAtLjA3MS4wMTItLjA5NC4wNDEtLjExNi4wMy0uMDIzLjEwMi0uMDUuMjM5LS4wNWgyLjQ4OGMuMTI0IDAgLjIzNS0uMDEuMzMyLS4wNzYuMDk3LS4wNjYuMTQ1LS4xODguMTQ1LS4zMTFWLjQxNmEuMzk2LjM5NiAwIDAgMC0uMTU3LS4zMjNBLjU4My41ODMgMCAwIDAgOC4xMzEgMHoiIGZpbGw9IiMwMDAiLz48cGF0aCBkPSJNMjAuOTU4IDE0LjQ3Yy0xLjQ4Mi40MTQtMi40ODkgMS4yNzMtMi40ODkgMi42ODR2NC4wNDJjMCAzLjAxNy0yLjkwOSAyLjY4NS02LjUxNyAyLjY4NWgtLjU2Yy0uMjIzIDAtLjM2My0uMDgzLS4zNjMtLjI3N1YyMi4yMmMwLS4xOTQuMTEyLS4yNzcuMzM2LS4yNzdoLjQ0N2MyLjE1NCAwIDMuNjY0LjQ3IDMuNjY0LTEuMjQ1di0zLjg3NmMwLTEuMTkuODQtMi44NTEgMi41MTctMy40Ni4xMTItLjAyOC4xNC0uMDgzLjE0LS4xMzggMC0uMDU2LS4wMjgtLjEzOS0uMTQtLjE5NC0xLjUzOC0uNjkyLTIuNTE3LTEuODI3LTIuNTE3LTMuMTg0VjUuNDczYzAtMS42ODktMS41MS0zLjM3Ny0zLjY2NC0zLjM3N2gtLjQ0N2MtLjIyNCAwLS4zMzYtLjA4My0uMzM2LS4yNzdWLjQzNWMwLS4xOTQuMTQtLjI3Ny4zNjQtLjI3N2guNTZjMy42MDcgMCA2LjU0NCAyLjU0NyA2LjU0NCA1LjU2NHYzLjY4MmMwIDEuMzg0IDEuMDA3IDIuMTg2IDIuNTE3IDIuNzEyLjU2LjE2Ni44NjcuMTk0Ljg2Ny42Mzd2MS4xNjNjLjAyOC4yNDktLjI1MS4zNi0uOTIzLjU1MyIgZmlsbD0iIzJlYWNlMyIvPjxwYXRoIGQ9Ik0xMS41NzYgOC4zM2MtLjQwNiAwLS43ODUuMzAzLS43ODUuNzJ2MS4zMjhjMCAuMzg5LjM1LjcyMS43ODUuNzIxaDEuNDgyYy40MDYgMCAuNzg0LS4zMDQuNzg0LS43MlY5LjA1YzAtLjM4OC0uMzQ4LS43Mi0uNzg0LS43MnoiIGZpbGw9IiMwMDAiLz48L3N2Zz4=
telegramtelegramdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEyIDI0YzYuNjI3IDAgMTItNS4zNzMgMTItMTJTMTguNjI3IDAgMTIgMCAwIDUuMzczIDAgMTJzNS4zNzMgMTIgMTIgMTJaIiBmaWxsPSJ1cmwoI2EpIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik01LjQyNSAxMS44NzFhNzk2LjQxNCA3OTYuNDE0IDAgMCAxIDYuOTk0LTMuMDE4YzMuMzI4LTEuMzg4IDQuMDI3LTEuNjI4IDQuNDc3LTEuNjM4LjEgMCAuMzIuMDIuNDcuMTQuMTIuMS4xNS4yMy4xNy4zMy4wMi4xLjA0LjMxLjAyLjQ3LS4xOCAxLjg5OC0uOTYgNi41MDQtMS4zNiA4LjYyMi0uMTcuOS0uNSAxLjE5OS0uODE5IDEuMjI5LS43LjA2LTEuMjI5LS40Ni0xLjg5OC0uOS0xLjA2LS42ODktMS42NDktMS4xMTktMi42NzgtMS43OTgtMS4xOS0uNzgtLjQyLTEuMjA5LjI2LTEuOTA4LjE4LS4xOCAzLjI0Ny0yLjk3OCAzLjMwNy0zLjIyOC4wMS0uMDMuMDEtLjE1LS4wNi0uMjEtLjA3LS4wNi0uMTctLjA0LS4yNS0uMDItLjExLjAyLTEuNzg4IDEuMTQtNS4wNTYgMy4zNDgtLjQ4LjMzLS45MDkuNDktMS4yOTkuNDgtLjQzLS4wMS0xLjI0OC0uMjQtMS44NjgtLjQ0LS43NS0uMjQtMS4zNDktLjM3LTEuMjk5LS43OS4wMy0uMjIuMzMtLjQ0Ljg5LS42NjlaIiBmaWxsPSIjZmZmIi8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMTEuOTkiIHkxPSIwIiB4Mj0iMTEuOTkiIHkyPSIyMy44MSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiMyQUFCRUUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMyMjlFRDkiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4K
travistravisdata:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNjYuNyIgaGVpZ2h0PSIyNjQuNSI+PHBhdGggZmlsbD0iI2NkMjQ0NSIgZD0iTTY0IDExNXMtNDIgMzAtNDMgNDFsMy0xczQ5LTMzIDg5LTM3bDEtNS01MCAybTY1LTQtMzMgMjMgMiAyIDU4LTE5IDEyLTctMzkgMW0yOCAyOGMyMyAwIDU4LTIyIDU4LTIybC0xMS0zaC0xOGwtOC0zLTIwIDIzLTIgNCAxIDFtLTk4IDg2LTMtMnptMTc0LTEzLTcgMi0zMy0xLTIxLTE2LTI1IDYtMjktMi0xNiAxNy0zMSAxMC0xNS01LTEtMSA3IDE3czE2IDE2IDI0IDE4YzkgMiAyNCAwIDM2LTIgMTItMSAyMS02IDI1LTEybDQtOXMxMSAxNiAyMSAxN2MxMCAyIDM4LTggMzgtOHMxOC00IDIxLTEwbDExLTI2LTkgNSIvPjxwYXRoIGZpbGw9IiNmMmYxOWIiIGQ9Ik0yNjEgOTNhNjYgNjYgMCAwIDAgMC00bC04LTZhMTA2IDEwNiAwIDAgMC0yMC05bC01LTItNS0yIDExIDNhMTQ0IDE0NCAwIDAgMSA2IDJjLTE2LTQzLTU0LTcwLTk2LTcwLTQzIDAtODEgMjctOTcgNzBhMTQ1IDE0NSAwIDAgMSAxNy01bC01IDJhMjAwIDIwMCAwIDAgMC0zMiAxN2wtMSAyYTcwIDcwIDAgMCAwIDAgMiA2OSA2OSAwIDAgMCAwIDYgNzkgNzkgMCAwIDAgMyAyMSA1NyA1NyAwIDAgMCAxIDUgNDMgNDMgMCAwIDAgMiA0bDEgMSAxIDEgNCAxLTMtMTIgMTYtM2E1MiA1MiAwIDAgMS0zLTFsLTYtMmEzMCAzMCAwIDAgMS0zLTFsLTMtMmMxMSAzIDMzIDIgNTMgMGE1MzggNTM4IDAgMCAxIDEwOCAwYzIwIDIgNDIgMyA1MyAwbC0zIDJhMzAgMzAgMCAwIDEtMyAxbC03IDItMSAxIDE4IDMtMyAxMWgybDEtMSAxLTFhMjIgMjIgMCAwIDAgMi00IDU2IDU2IDAgMCAwIDItNSA3OCA3OCAwIDAgMCAyLTIxIDY4IDY4IDAgMCAwIDAtNiIvPjxwYXRoIGZpbGw9IiNlNWM5YTMiIGQ9Ik0xNTYgMjQ0YTU4IDU4IDAgMCAxLTUgMGgtM2E3NzYgNzc2IDAgMCAwIDMtNiAxOTggMTk4IDAgMCAwIDUgNm0zIDNjNCA0IDEwIDcgMTYgNy0xMCA0LTIwIDYtMjcgNi04IDEtMTUgMC0yMi0yYTI3IDI3IDAgMCAxIDEgMGMxLTEgMTQtMiAyMC0xMWg1YTU4IDU4IDAgMCAwIDYtMWwxIDEiLz48cGF0aCBmaWxsPSIjNWQ2NzYyIiBkPSJNMTcxIDExNmExMjggMTI4IDAgMCAxLTEyIDEzIDQ5MyA0OTMgMCAwIDAtMTUgMGwtMjQgMWExOTcgMTk3IDAgMCAxIDUxLTE0bS02NSA1LTEyIDExYTQ4MCA0ODAgMCAwIDAtMjkgM2MxMi01IDI2LTEwIDQxLTE0bTEzNiAyMy01IDMyLTIxIDE1LTU3LTctOC0yOGEyIDIgMCAwIDAtMS0xIDM1IDM1IDAgMCAwLTExIDAgMiAyIDAgMCAwLTIgMWwtOCAyOC01NiAxMi0yMi0xNi01LTM1YTI2NyAyNjcgMCAwIDEgMy0yaDVsNCAzMyAxIDEgMTUgMTFhMiAyIDAgMCAwIDEgMGw0Ni0xMGgxYTIgMiAwIDAgMCAxLTFsOC0yOCAxMy0yIDEzIDIgOCAyOCAyIDEgNDYgNWgxbDE1LTExIDEtMSA0LTI5IDggMm02LTIwLTQgMTVjLTgtMi0yNi01LTUxLTdsMjQtMTMgMzEgNSIvPjxwYXRoIGZpbGw9IiNlNGM4OTYiIGQ9Im0xNTQgMTM0LTcgNS04IDVhNjkgNjkgMCAwIDAtMTAgMiAyIDIgMCAwIDAtMSAxbC04IDI4LTQ1IDktMTQtOS00LTMzIDMyLTVhNzYgNzYgMCAwIDAtNCA1bC04IDExIDExLTdzNy01IDE5LTEwYTUwMyA1MDMgMCAwIDEgNDctMnptLTY5IDM3IDEyLTFhNDAgNDAgMCAwIDAgNCAwYzYgMCAxMCAwIDEwLTlzLTQtMTYtOS0xNmMtNiAwLTEyIDctMTEgMTZsMSA2Yy01IDEtNyA0LTcgNG0xNDYtMjktNCAyOC0xNCA5LTQ1LTUtOC0yN2EyIDIgMCAwIDAtMS0xbC0xMi0yaDFjMSAwIDE3LTIgMzUtOCAyMCAxIDM2IDQgNDggNnptLTI5IDI2YzAtMS0xLTQtNi00bDEtNWMtMS05LTYtMTYtMTItMTZzLTkgNy05IDE2IDUgOSAxMSA5YzcgMCAxMi0yIDE1IDAiLz48cGF0aCBmaWxsPSIjYzRhZjkwIiBkPSJtMTU0IDEzNC03IDUtOCA1YTY5IDY5IDAgMCAwLTEwIDIgMiAyIDAgMCAwLTEgMWwtOCAyOC00NSA5LTE0LTktNC0zMyAzMi01YTc2IDc2IDAgMCAwLTQgNWwtOCAxMSAxMS03czctNSAxOS0xMGE1MDMgNTAzIDAgMCAxIDQ3LTJ6bS02OSAzNyAxMi0xYTQwIDQwIDAgMCAwIDQgMGM2IDAgMTAgMCAxMC05cy00LTE2LTktMTZjLTYgMC0xMiA3LTExIDE2bDEgNmMtNSAxLTcgNC03IDRtMTQ2LTI5LTQgMjgtMTQgOS00NS01LTgtMjdhMiAyIDAgMCAwLTEtMWwtMTItMmgxYzEgMCAxNy0yIDM1LTggMjAgMSAzNiA0IDQ4IDZ6bS0yOSAyNmMwLTEtMS00LTYtNGwxLTVjLTEtOS02LTE2LTEyLTE2cy05IDctOSAxNiA1IDkgMTEgOWM3IDAgMTItMiAxNSAwIi8+PHBhdGggZmlsbD0iI2U1YzlhMyIgZD0ibTI1MCAxNTQgMSA5Yy0xIDgtNSAyMi03IDI1bC0xMC0xIDEtNSA1LTQgMS0xIDQtMjYgNSAzTTU1IDE4OHYzbC0xMSAxYy0yLTItNi0xNy03LTI1di05bDctMyAzIDI3IDEgMSA3IDVtMTA3IDB2MnMtNyA2LTE2IDdjLTEwIDEtMTgtNS0xOC01bDMgNmE3MSA3MSAwIDAgMC04LTFoLTRjLTYgMC0xNCAxMC0yMCAxOWwtMjMgN2MtMTAtMTQtMTUtMjgtMTUtMjlsLTEtMiAzIDIgNiA0IDIgMmEyIDIgMCAwIDAgMiAwbDU4LTEzYTIgMiAwIDAgMCAxLTFsOC0yN2EyOCAyOCAwIDAgMSA4IDBsOCAyOCAyIDFoNCIvPjxwYXRoIGZpbGw9IiNlNWM5YTMiIGQ9Im0yMjggMTg3IDItMmExMjAgMTIwIDAgMCAxLTEwIDI3aC0zbC04LTEtMTYtMi0xNi0xMi0xLTFjLTEtMS0yLTItNS0ybC0xNCAzYzUtMyA1LTcgNS03di0ybDU0IDZhMiAyIDAgMCAwIDEgMGw2LTQgNS0zIi8+PHBhdGggZmlsbD0iI2U5ZDU4NiIgZD0iTTE4OCA3MmMwIDUgMCAxNC0yIDIyYTIgMiAwIDAgMCAwIDEgMzQ2IDM0NiAwIDAgMC05LTFjMy02IDQtMTQgNC0xNmw3LTZtLTgyIDZjMCAyIDEgMTAgNCAxNmEzMzMgMzMzIDAgMCAwLTkgMGMtMi04LTItMTctMi0yMmw3IDYiLz48cGF0aCBmaWxsPSIjMmEyYzMwIiBkPSJNMTg0IDE1NGEzIDMgMCAwIDAgMy0zIDMgMyAwIDAgMC0zLTIgMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAgMyAyem0xOCAxNGMtMy0yLTggMC0xNSAwLTYgMC0xMSAwLTExLTlzMy0xNiA5LTE2IDExIDcgMTIgMTZsLTEgNWM1IDAgNiAzIDYgNCIvPjxwYXRoIGZpbGw9IiNmMWZhZmMiIGQ9Ik0xODQgMTQ5YTMgMyAwIDEgMSAwIDUgMyAzIDAgMSAxIDAtNSIvPjxwYXRoIGZpbGw9IiMyYTJjMzAiIGQ9Ik0xMDIgMTU3YTMgMyAwIDEgMCAwLTYgMyAzIDAgMCAwIDAgNnptOSA0YzAgOS00IDktMTAgOWgtNGwtMTIgMXMyLTMgNy00bC0xLTZjLTEtOSA1LTE2IDExLTE2IDUgMCA5IDcgOSAxNiIvPjxwYXRoIGZpbGw9IiNmMWZhZmMiIGQ9Ik0xMDIgMTUxYTMgMyAwIDEgMSAwIDYgMyAzIDAgMCAxIDAtNiIvPjxwYXRoIGZpbGw9IiNlYmRiOGIiIGQ9Im02NiAxMDEtMS0xdi0zbDItMjAgMzAtNyAyIDI1Yy0xLTItNC02LTUtMTMtMS0zLTMtNC02LTRsLTcgMWMtNCAxLTEwIDMtMTEgNS0yIDUtMiAxNi0yIDE2bC0yIDFtMTU1IDAtMS0xcy0xLTExLTMtMTZjLTEtMi03LTQtMTEtNWwtNy0xYy0zIDAtNSAxLTYgNC0xIDctMyAxMS01IDEzbDItMjUgMzAgNyAyIDE3djZsLTEgMSIvPjxwYXRoIGZpbGw9IiNlYmRjOGMiIGQ9Im0xNzQgOTctNjAtMS0zLTctMy0xMi01LTRWNDVsNy0yMHMxIDYwIDE1IDYwaDM3YzE0IDAgMTUtNjAgMTUtNjBsMTAgMzQtNyAxOS0xIDUtMiA5LTMgNW02MiAxMC0zLTEgMyAxbS0zLTEtMTEtNFY4MXMzIDIxIDExIDI1Ii8+PHBhdGggZmlsbD0iI2VhZDY4NyIgZD0iTTIyMiAxMDB2LTYgNiIvPjxwYXRoIGZpbGw9IiNlYmRjOGMiIGQ9Im01MSAxMDcgNC0xLTQgMW00LTFjNy00IDEwLTI1IDEwLTI1djIxbC0xMCA0Ii8+PHBhdGggZmlsbD0iI2VhZDY4NyIgZD0iTTY1IDEwMHYtMyAzIi8+PHBhdGggZmlsbD0iIzJhMmMzMCIgZD0iTTk4IDk2di0xYy0zLTktMy0xOS0yLTI0bC0yOCA4Yy0yIDEyLTEgMjAgMCAyMmExODUgMTg1IDAgMCAxIDYtMiAyMjkgMjI5IDAgMCAxIDIzLTNoMXptMTItMmMtMy02LTQtMTQtNC0xNmwtNy02YTc3IDc3IDAgMCAwIDIgMjNsOS0xem03NiAwYzItOCAyLTE3IDItMjJsLTcgNmMwIDItMSAxMC00IDE2bDkgMWEyIDIgMCAwIDEgMC0xem0zNCA3YzAtMSAxLTEwLTEtMjJsLTI4LThjMSA1IDEgMTUtMiAyNHYxaDFhMjI4IDIyOCAwIDAgMSAzMCA1em00IDEgMTEgNS02LTJhOTQgOTQgMCAwIDAtNS0xIDE3NSAxNzUgMCAwIDAtMjMtM2wtMTItMWE3NjEgNzYxIDAgMCAwLTkxIDBsLTEyIDFhMjQyIDI0MiAwIDAgMC0zNCA2bDExLTVoMWMwLTEtMS0xMSAxLTI1YTIgMiAwIDAgMSAxLTFsNC0yYzEtMzAgMTMtNDQgMTQtNDRhODMgODMgMCAwIDAtMTEgNDRsMjQtN2gybDIgMmMtMS04IDAtMzEgMTEtNTAtMSAxLTkgMjYtNiA1NGE0NSA0NSAwIDAgMCAzIDJ2MnMxIDkgNSAxN2E0NDQgNDQ0IDAgMCAxIDU5IDBjNC04IDUtMTcgNS0xN2EyIDIgMCAwIDEgMC0yIDQ5IDQ5IDAgMCAwIDMtMmMzLTI4LTUtNTMtNS01NCAxMCAxOSAxMSA0MiAxMSA1MGE4MyA4MyAwIDAgMCAxLTJoMmwyNCA3YzAtNSAwLTI2LTExLTQ0IDEgMCAxMyAxNCAxNCA0NGw0IDJhMiAyIDAgMCAxIDEgMWMzIDE0IDEgMjQgMSAyNWgxIi8+PHBhdGggZmlsbD0iIzJhMmMzMCIgZD0iTTE2OCA0M1YzMGgtNDd2MTNoNnYtNmgxM3Y0MGgtNXY3aDE4di03aC01VjM3aDEzdjZ6bTQtMTh2MjNoLTE2di03aC0zdjMxaDV2MTdoLTI4VjcyaDVWNDFoLTN2N2gtMTZWMjVoNTYiLz48cGF0aCBmaWxsPSIjY2QyNDQ1IiBkPSJNMTY4IDMwdjEzaC03di02aC0xM3Y0MGg1djdoLTE4di03aDVWMzdoLTEzdjZoLTZWMzBoNDciLz48cGF0aCBmaWxsPSIjNWQ2NzYyIiBkPSJtNDEgMTI0IDktMmExMzkgMTM5IDAgMCAwLTggNmwtMS00Ii8+PHBhdGggZmlsbD0iI2M0YWY5MCIgZD0iTTEyNyAxODZzMCA1LTUgNmMtNiAyLTQ0IDEzLTQ4IDEzbC0xNS04LTMtOSAxNiAxMCA1NS0xMm0zMyAwczAgNCA2IDVsNDggMTFjNC0xIDE1LTkgMTUtOWwyLTktMTUgOC01Ni02Ii8+PHBhdGggZmlsbD0iI2M0YWY5MCIgZD0iTTM4IDE3OXM1IDEwIDEzIDZsNiAzdjRsLTEzIDItMy0yLTMtMTNtMjEyLTNzLTggMTEtMTcgN2wtMiAydjRsMTMgMiAzLTIgMy0xM20tMTMxIDgxczMxIDE2IDY2LTVsLTEyLTFzLTI0IDEwLTQzIDRsLTExIDIiLz48cGF0aCBmaWxsPSIjMmEyYzMwIiBkPSJNNTAgMTIyYTMwNSAzMDUgMCAwIDAtOSAybDEgNGExMzkgMTM5IDAgMCAxIDgtNnptNSA2OWE5MSA5MSAwIDAgMSAwLTNsLTctNWEyIDIgMCAwIDEtMS0xbC0zLTI3LTcgM3Y5YzEgOCA1IDIzIDcgMjVsMTEtMXptNDItNzMgMTUtM2ExMTczIDExNzMgMCAwIDAtNDUgMmMtNiA0LTI4IDE4LTQzIDM2IDE1LTEwIDQwLTI2IDczLTM1em0tMyAxNCAxMi0xMWMtMTUgNC0yOSA5LTQxIDE0YTQ4MSA0ODEgMCAwIDEgMjktM3ptNTgtMThoLTIzbC04IDRjLTEgMS0xMiA2LTIzIDE2IDktNSAyMy0xMSAzOS0xNmExOTIgMTkyIDAgMCAxIDE1LTR6bS01IDI1IDctNWE1NjMgNTYzIDAgMCAwLTEwIDAgNTAyIDUwMiAwIDAgMC0zNyAyYy0xMiA1LTE5IDEwLTE5IDEwbC0xMSA3IDgtMTFhNzAgNzAgMCAwIDEgNC01bC0zMiA1IDQgMzMgMTQgOSA0NS05IDgtMjhhMiAyIDAgMCAxIDEtMWwxMC0yem00IDEwNWg1YTE4NSAxODUgMCAwIDEtNS02IDUzMSA1MzEgMCAwIDEtMyA2aDN6bTI0IDEwYTI1IDI1IDAgMCAxLTE3LThsLTYgMWgtNWMtNiA5LTE5IDEwLTIwIDExaC0xYzcgMiAxNCAzIDIyIDIgNyAwIDE3LTIgMjctNnptLTUtMTM2IDEtMmExNTAgMTUwIDAgMCAwLTEzIDJjLTE0IDMtMjcgNy0zOCAxMmE1MTEgNTExIDAgMCAxIDI0LTFoMTVsMTEtMTF6bTQwLTFhNzM1IDczNSAwIDAgMC0xMy0xbC0xOS0xLTIgM2MtMyA0LTEwIDEzLTE4IDE5IDEyLTIgMzMtNyA1MC0xOWwyLTF6bTIwIDY4LTIgMi01IDMtNiA0YTIgMiAwIDAgMS0xIDBsLTU0LTZoLTRhMiAyIDAgMCAxLTItMWwtOC0yOGEyOCAyOCAwIDAgMC04IDBsLTggMjdhMiAyIDAgMCAxLTEgMWwtNTggMTNoLTJsLTItMi02LTQtMy0yIDEgMmMwIDEgNSAxNSAxNSAyOWExMTY2IDExNjYgMCAwIDAgMjMtN2M2LTkgMTQtMTkgMjAtMTlhNjggNjggMCAwIDEgNCAwbDggMS0zLTZzOCA2IDE4IDVjOS0xIDE2LTcgMTYtN3MwIDQtNSA3bDE0LTNjMyAwIDQgMSA1IDJsMSAxYTQ2MjYgNDYyNiAwIDAgMSAxNiAxMiA4MTYgODE2IDAgMCAwIDI3IDMgMTM0IDEzNCAwIDAgMCAxMC0yN3ptLTMtMTUgNC0yOGMtMTItMi0yOC01LTQ4LTYtMTggNi0zNCA4LTM1IDhoLTFsMTIgMiAxIDEgOCAyNyA0NSA1em0xMCA2IDUtMzJhMzIxIDMyMSAwIDAgMC04LTJsLTQgMjlhMiAyIDAgMCAxLTEgMWwtMTUgMTFoLTFsLTQ2LTVhMiAyIDAgMCAxLTItMWwtOC0yOC0xMy0yLTEzIDItOCAyOGEyIDIgMCAwIDEtMiAxbC00NiAxMGgtMWwtMTUtMTFhMiAyIDAgMCAxLTEtMWwtNC0zM2EzMjkgMzI5IDAgMCAwLTUgMGwtMyAyIDUgMzUgMjIgMTYgNTYtMTIgOC0yOCAyLTFhMzUgMzUgMCAwIDEgMTEgMGwxIDEgOCAyOCA1NyA3em03LTM3IDQtMTUtMzEtNS0yNCAxM2MyNSAyIDQzIDUgNTEgN3ptNyAyNC0xLTktNS0zLTQgMjYtMSAxLTUgNC0xIDUgMTAgMWMyLTMgNi0xNyA3LTI1em0xNi02NC0xIDExYTc0IDc0IDAgMCAxLTIgMTIgNjAgNjAgMCAwIDEtMiA1IDUxIDUxIDAgMCAxLTEgM2wtMSAyLTEgMXYxaC0xYTQ1IDQ1IDAgMCAxLTEgMWwtMiAxLTQgMWE4NCA4NCAwIDAgMC0yIDFsLTIgN2gtMWwtMSAxIDggNGMzIDIgMyA2IDMgMTNsLTQgMTdjLTIgMTEtNCAxMy02IDEzYTE3IDE3IDAgMCAxLTQgMWwtMTAtMWMwIDMtMyA5LTcgMTdoMWwxNS03IDctMy0zIDctMTQgMjZjLTUgMTAtMTQgMTItMjAgMTNsLTIgMS0yMCA1YTg1IDg1IDAgMCAxLTE1IDdjLTE5IDctMzkgNy01OCAwbC0xMiAyYTYwIDYwIDAgMCAxLTcgMGMtMTggMC0yNy05LTM0LTE4bC0xNi0yNC02LTggOSA0IDIwIDEwIDEtMWMtNi04LTExLTE4LTE0LTI5bC0xMSAyaC0zYy0yLTEtNC0zLTgtMTNsLTMtMTdjLTEtNy0xLTEwIDItMTNsOS01di0yYy0xOSAxMS0zMCAyMC0zMCAyMUwwIDE3OWw5LTE1YzgtMTEgMTYtMjEgMjQtMjhoLTFsLTItMWE0NyA0NyAwIDAgMS0xLTFoLTF2LTFsLTEtMS0xLTJhNDEgNDEgMCAwIDEtMS0zIDYwIDYwIDAgMCAxLTItNSA3MyA3MyAwIDAgMS0yLTEyIDczIDczIDAgMCAxIDAtMTEgNjkgNjkgMCAwIDEgMS0xMnYtMWgxYzMtMyA2LTQgMTAtNmExMDUgMTA1IDAgMCAxIDgtM2M4LTIyIDIyLTQyIDQwLTU2YTEwNCAxMDQgMCAwIDEgMTI2IDBjMTcgMTQgMzEgMzQgMzkgNTZsOCAzYzQgMiA3IDMgMTAgNmgxdjFhMzYgMzYgMCAwIDEgMSA2bDEgNnpNNTQgNzRhMTkyIDE5MiAwIDAgMC0yNyAxNWwtMSAyYTY4IDY4IDAgMCAwIDAgMiA3MCA3MCAwIDAgMCAwIDYgODAgODAgMCAwIDAgNiAzMGwxIDEgMSAxIDQgMS0zLTEyIDE2LTNhNDUgNDUgMCAwIDEtMy0xIDQ5IDQ5IDAgMCAxLTktM2wtMy0yYzExIDMgMzMgMiA1MyAwYTU0MCA1NDAgMCAwIDEgMTA4IDBjMjAgMiA0MiAzIDUzIDBsLTMgMi0zIDEtNyAyLTEgMSAxOCAzLTMgMTFoMmwxLTEgMS0xYTI4IDI4IDAgMCAwIDItNCA1NiA1NiAwIDAgMCAyLTUgNzcgNzcgMCAwIDAgMi0yMSA3MCA3MCAwIDAgMCAwLTYgNjkgNjkgMCAwIDAgMC0ydi0ybC04LTZhMTA3IDEwNyAwIDAgMC0yMC05bC01LTItNS0yYTEyNCAxMjQgMCAwIDEgMTcgNWMtMTYtNDMtNTQtNzAtOTYtNzAtNDMgMC04MSAyNy05NyA3MGExNDQgMTQ0IDAgMCAxIDE3LTUgMjQ4IDI0OCAwIDAgMC0xMCA0em05NiAxNTUgMiAyIDExIDEyYzQgMyA4IDUgMTQgNWg1bDYtMmE2OTQgNjk0IDAgMCAwIDIyLTVjNS0xIDEyLTMgMTYtMTFsMTAtMTgtNyAzYy0zIDItNyAyLTEyIDJoLTlsLTE2LTJoLTFsLTEtMS0xNy0xM3YtMWgtMmwtMjQgN2gtMnMtMTAtNC0yMi00aC0zYy0xIDAtOCAzLTE3IDE3djFoLTFhMTA2MCAxMDYwIDAgMCAxLTMxIDlsLTEgMS0xLTEtMTItNSAxMSAxNWM2IDggMTQgMTUgMjkgMTVoNmwyMi0zaDJzMTItMiAxNi0xMGw0LTEwIDItM3YtMmwxIDIiLz48L3N2Zz4=

Feel free to reach out to us if you have any questions, and happy badging!

Simple Icons 13

· One min read
chris48s
Shields.io Core Team

Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 13. This release removes 65 icons and renames one. A full list of the changes can be found in the release notes.

Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the SimpleIcons project.

Simple Icons 12

· One min read
chris48s
Shields.io Core Team

Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 12. This release removes the following 10 icons:

  • FITE
  • Flattr
  • Google Bard
  • Integromat
  • Niantic
  • Nintendo Network
  • Rome
  • Shotcut
  • Skynet
  • Twitter

And renames the following 3:

  • Airbrake.io to Airbrake
  • Amazon AWS to Amazon Web Services
  • RStudio to RStudio IDE

More details can be found in the release notes.

Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the SimpleIcons project.

Simple Icons 11

· One min read
chris48s
Shields.io Core Team

Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 11. This release removes the following 4 icons:

  • Babylon.js
  • Hulu
  • Pepsi
  • Uno

More details can be found in the release notes.

Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the SimpleIcons project.

Simple Icons 10

· One min read
chris48s
Shields.io Core Team

Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 10. This release removes 45 icons. A full list of the removals can be found in the release notes.

Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the SimpleIcons project.

Applying filters to GitHub Tag and Release badges

· One min read
chris48s
Shields.io Core Team

We recently shipped a feature which allows you to pass an arbitrary filter to the GitHub tag and release badges. The filter param can be used to apply a filter to the project's tag or release names before selecting the latest from the list. Two constructs are available: * is a wildcard matching zero or more characters, and if the pattern starts with a !, the whole pattern is negated.

To give an example of how this might be useful, we create two types of tags on our GitHub repo: https://github.com/badges/shields/tags There are tags in the format major.minor.patch which correspond to our NPM package releases and tags in the format server-YYYY-MM-DD that correspond to our docker snapshot releases.

In our case, this would allow us to make a badge that applies the filter !server-* to filter out the snapshot tags and just select the latest package tag.

We launched a new frontend

· One min read
chris48s
Shields.io Core Team

Alongside the general visual refresh and improvements to look and feel, our new frontend has allowed us to address a number of long-standing feature requests and enhancements:

  • Clearer and more discoverable documentation for our static, dynamic json/xml/yaml and endpoint badges
  • Improved badge builder interface, with all optional query parameters included in the builder for each badge
  • Each badge now has its own documentation page, which we can link to. e.g: https://shields.io/badges/discord
  • Light/dark mode themes
  • Improved search
  • Documentation for individual path and query parameters

The new site also comes with big maintenance benefits for the core team. We rely heavily on docusaurus, docusaurus-openapi, and docusaurus-search-local. This moves us to a mostly declarative setup, massively reducing the amount of custom frontend code we maintain ourselves.