Pular para o conteúdo
SheetForge

Mercado de Plugins Web

O aplicativo web tem um mercado de plugins para instalar plugins do SheetForge no navegador, além de um caminho separado para fazer sideload de um plugin não revisado a partir de uma URL do GitHub. O mesmo mercado, reconstruído em UIToolkit, também é distribuído dentro da Unity.

O registro — um arquivo estático, dois leitores

A listagem do market vem de um único arquivo de registro estático (public/registry/plugins.json) que tanto o aplicativo web quanto a janela de market dentro da Unity leem. Existe uma lista, então as duas superfícies sempre mostram o mesmo catálogo.

O SheetForge não hospeda os binários dos plugins. O GitHub release de cada autor faz isso. A entrada do registro é metadado mais um hash fixado — não uma cópia do plugin.

Fixação de hash — integridade antes de o core ver um byte

Toda entrada de registro carrega o SHA-256 capturado quando o plugin foi aprovado. Na aquisição, todo byte recebido é verificado contra esse hash antes de sequer alcançar o core. Uma diferença de um único byte é recusada.

Isso é o que torna "o binário vive no GitHub de outra pessoa" seguro: o release pode ser rebaixado de qualquer lugar, mas apenas os bytes exatos aprovados são carregados.

Instalação em um clique através do proxy do servidor

Navegadores não conseguem buscar assets de release do GitHub diretamente — regras de origem cruzada (CORS) bloqueiam isso. Então a aquisição passa por uma rota de proxy do servidor (api/market/artifact, conectada via o ArtifactProxy em lib/market/install.ts), que busca o release do lado do servidor e o transmite de volta. A instalação é, portanto, em um clique.

Um caminho manual de baixar e enviar permanece como um fallback secundário para quando o proxy não consegue alcançar um release. Ele passa pela mesma checagem de hash — um arquivo enviado não é mais confiável do que um vindo pelo proxy.

O portão de compatibilidade — depois da integridade, antes do registro

Passar na checagem de hash prova que os bytes são os aprovados. Isso não diz nada sobre se este host consegue ler o formato daquele plugin, o que é uma segunda pergunta, separada.

A resposta vem da própria DLL verificada: uma declaração em nível de assembly nomeando a geração do formato de plugin para a qual ela foi construída, e a menor versão de host que ela quer.

  • A listagem anuncia, o assembly decide. Uma entrada de registro carrega os mesmos dois valores (pluginFormat, minHost), então o catálogo pode mostrá-los antes de você baixar. O próprio portão lê a declaração a partir dos bytes que acabaram de passar na verificação. Uma listagem pode estar desatualizada; uma declaração compilada não pode.
  • Um portão, três caminhos. Instalação pelo market, sideload e um simples arquivo local, todos perguntam ao mesmo predicado, então nenhuma rota consegue aceitar silenciosamente o que outra recusa.
  • Tudo ou nada por assembly. Um assembly recusado registra nada — nunca metade dos seus contratos — e a recusa nomeia o que o assembly declarou contra o que este host lê.
  • Nenhuma declaração é aceitável. Um plugin construído antes de o atributo existir é lido como a geração mais antiga, sem exigência de host, então ele carrega exatamente como sempre carregou.

Cache e descarregamento

Bytes que passam na checagem de hash são armazenados em cache no navegador (IndexedDB), então um plugin habilitado na lista carrega automaticamente em visitas futuras — você o habilita uma vez.

Desabilitar ou remover um plugin tem efeito imediato: o aplicativo manda o núcleo tirar esse assembly da composição de plugins e reconstruir a sessão, de modo que seus tipos de célula, enums, modelos, predefinições de cor, ações do estúdio e sobreposições do canvas desaparecem sem refresh. (O próprio .NET nunca descarrega um assembly — os bytes continuam residentes na página, inertes; se você habilitar o mesmo plugin de novo, o núcleo reativa o assembly residente em vez de carregá-lo uma segunda vez.)

Fazendo sideload de um plugin não revisado a partir de uma URL do GitHub

Separadamente do registro curado, você pode entregar ao app uma URL arbitrária do GitHub para fazer sideload de um plugin não revisado (lib/market/sideload.ts, servido por api/market/github). Isso serve para experimentar um plugin que não está no catálogo.

Como este eixo recebe uma URL fornecida pelo usuário, em vez de uma entrada de registro examinada, ele é o único lugar que impõe defesa contra SSRF diretamente:

  • A URL precisa resolver para um host da família GitHubgithub.com, raw.githubusercontent.com, release-assets.githubusercontent.com e afins — comparado exatamente, em minúsculas.
  • Redirecionamentos que vazariam para uma rede interna são bloqueados nessa fronteira.

Artefatos de sideload são armazenados em cache sob uma tag de dono distinta (sideload:). Então um plugin não revisado que por acaso compartilhe bytes com uma entrada de registro é mantido separado do curado — um sideload nunca se disfarça de plugin aprovado.

GitHub como origem de importação

Os dois caminhos roteiam os bytes do plugin através do mesmo pipeline:

  • A instalação pelo registro puxa o artefato hospedado no release do GitHub do autor através do proxy do servidor, depois o verifica contra o SHA-256 fixado.
  • O sideload aceita uma URL bruta do GitHub, protegida para hosts da família GitHub, depois verifica os bytes da mesma forma.

Nos dois casos, a limitação de CORS do navegador sobre assets de release do GitHub é tratada do lado do servidor, enquanto a integridade é preservada pela checagem de hash no cliente antes de o core carregar qualquer coisa.

O market dentro da Unity

O market de plugins da Unity foi reconstruído em UIToolkit para se parecer com o market web — uma grade de cards ao lado de um painel de detalhes, lendo o mesmo registro estático, com a mesma rejeição por hash divergente e o mesmo aviso de instalação de confiança total.

O login e a lista de plugins guardada no navegador, junto com as predefinições, permanecem exclusivos da web; a janela da Unity é a superfície de catálogo e instalação.

Páginas relacionadas