Chrome扩展在安装时显示CRX_REQUIRED_PROOF_MISSING

62lalag4  于 2023-04-27  发布在  Go
关注(0)|答案(3)|浏览(593)

嗨,我刚刚创建了一个公司内部使用的Chrome扩展,我不想发布,并希望传递给我的同事,当我在我的本地Chrome中打包该扩展时,它给了我错误,说CRX_REQUIRED_PROOF_MISSING我没有得到足够的关于这个错误的东西,请在这个问题上帮助我。

izj3ouym

izj3ouym1#

你不能发布不在Chrome扩展商店中的扩展。根据官方的Chrome文档,从Chrome扩展商店或外部发布的每个扩展都必须上传到Chrome扩展商店。如果你想在商店之外发布扩展,在你上传之后,我认为你应该创建一个修改注册表的脚本,它会为你安装它。

fdbelqdn

fdbelqdn2#

您需要修改本地策略以允许从您需要指定的自定义URL库进行安装。此信息在Linux上保存在JSON中,在Windows上保存在注册表中。请在此处查看此链接Set Chrome app and extension policies (Windows),然后单击Extension Install Sources以了解如何将扩展的URL列入白名单。然后使用Extension Install Allowlist启用特定的扩展ID。
同样要获得稳定的扩展ID,请使用Chrome打包器,这意味着使用命令行chrome --pack-extension="path\to\extension\folder" --pack-extension-key="path\to\file.pem"执行chrome。只要.pem被重用,这将产生一个具有稳定ID的适当的.crx,您可以将其列入白名单,并在更新时保持不变。
要从.CRX读取ID,这是我的C#代码:

private static string ReadExtensionIdFromCrx3(string path)
{
    using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
    return ReadExtensionIdFromCrx3(stream);
}

private static string ReadExtensionIdFromCrx3(Stream stream)
{
    using var reader = new BinaryReader(stream);
    using var netreader = new BinaryReaderNetOrder(stream);
    var magic = netreader.ReadInt32();
    if (magic != 0x43723234)
    {
        throw new InvalidDataException();
    }

    var version = netreader.ReadInt32();
    if (version != 0x03000000)
    {
        throw new InvalidDataException();
    }

    var headerSize = reader.ReadInt32();
    var headerBytes = reader.ReadBytes(headerSize);

    var header = CrxFileHeader.Parser.ParseFrom(headerBytes);
    var first = header.Sha256WithRsa[0];
    var halfHashed = new byte[16];
    using (var hash = SHA256.Create())
    {
        var hashed = hash.ComputeHash(first.PublicKey.ToByteArray());
        Array.Copy(hashed, 0, halfHashed, 0, halfHashed.Length);
    }

    var sb = new StringBuilder();
    for (int it = 0; it < halfHashed.Length; ++it)
    {
        sb.Append((char)('a' + (halfHashed[it] / 16)));
        sb.Append((char)('a' + (halfHashed[it] % 16)));
    }
    return sb.ToString();
}

你也可以使用这个极简的网络顺序字节阅读器。

public class BinaryReaderNetOrder : BinaryReader
{
    public BinaryReaderNetOrder(Stream stream) : base(stream) { }

    public override short ReadInt16()
    {
        return BitConverter.ToInt16(ReadNetOrder(2), 0);
    }

    public override int ReadInt32()
    {
        return BitConverter.ToInt32(ReadNetOrder(4), 0);
    }

    public override long ReadInt64()
    {
        return BitConverter.ToInt64(ReadNetOrder(8), 0);
    }

    public override ushort ReadUInt16()
    {
        return BitConverter.ToUInt16(ReadNetOrder(2), 0);
    }

    public override uint ReadUInt32()
    {
        return BitConverter.ToUInt32(ReadNetOrder(4), 0);
    }

    public override ulong ReadUInt64()
    {
        return BitConverter.ToUInt64(ReadNetOrder(8), 0);
    }

    public override float ReadSingle()
    {
        return BitConverter.ToSingle(ReadNetOrder(4), 0);
    }

    public override double ReadDouble()
    {
        return BitConverter.ToDouble(ReadNetOrder(8), 0);
    }

    public override byte[] ReadBytes(int count)
    {
        var data = base.ReadBytes(count);
        if (data.Length < count)
        {
            throw new EndOfStreamException();
        }
        if (data.Length > count)
        {
            throw new IOException();
        }
        return data;
    }

    private byte[] ReadNetOrder(int count)
    {
        if (count < 2 || count > 8)
        {
            throw new ArgumentOutOfRangeException(nameof(count));
        }

        var data = ReadBytes(count);
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(data);
        }
        return data;
    }
}

您还需要Protobuf头定义:

// <auto-generated>
//     Generated by the protocol buffer compiler.  DO NOT EDIT!
//     source: test.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021
#region Designer generated code

using pb = Google.Protobuf;
using pbc = Google.Protobuf.Collections;
using pbr = Google.Protobuf.Reflection;
using scg = System.Collections.Generic;
/// <summary>Holder for reflection information generated from test.proto</summary>
public static partial class TestReflection
{

    #region Descriptor
    /// <summary>File descriptor for test.proto</summary>
    public static pbr::FileDescriptor Descriptor
    {
        get { return descriptor; }
    }
    private static pbr::FileDescriptor descriptor;

    static TestReflection()
    {
        byte[] descriptorData = System.Convert.FromBase64String(
            string.Concat(
              "Cgp0ZXN0LnByb3RvIooBCg1DcnhGaWxlSGVhZGVyEiwKD3NoYTI1Nl93aXRo",
              "X3JzYRgCIAMoCzITLkFzeW1tZXRyaWNLZXlQcm9vZhIuChFzaGEyNTZfd2l0",
              "aF9lY2RzYRgDIAMoCzITLkFzeW1tZXRyaWNLZXlQcm9vZhIbChJzaWduZWRf",
              "aGVhZGVyX2RhdGEYkE4gASgMIjsKEkFzeW1tZXRyaWNLZXlQcm9vZhISCgpw",
              "dWJsaWNfa2V5GAEgASgMEhEKCXNpZ25hdHVyZRgCIAEoDCIcCgpTaWduZWRE",
              "YXRhEg4KBmNyeF9pZBgBIAEoDGIGcHJvdG8z"));
        descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
            new pbr::FileDescriptor[] { },
            new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] {
      new pbr::GeneratedClrTypeInfo(typeof(CrxFileHeader), CrxFileHeader.Parser, new[]{ "Sha256WithRsa", "Sha256WithEcdsa", "SignedHeaderData" }, null, null, null, null),
      new pbr::GeneratedClrTypeInfo(typeof(AsymmetricKeyProof), AsymmetricKeyProof.Parser, new[]{ "PublicKey", "Signature" }, null, null, null, null),
      new pbr::GeneratedClrTypeInfo(typeof(SignedData), SignedData.Parser, new[]{ "CrxId" }, null, null, null, null)
            }));
    }
    #endregion

}
#region Messages
public sealed partial class CrxFileHeader : pb::IMessage<CrxFileHeader>
{
    private static readonly pb::MessageParser<CrxFileHeader> _parser = new pb::MessageParser<CrxFileHeader>(() => new CrxFileHeader());
    private pb::UnknownFieldSet _unknownFields;
    [DebuggerNonUserCode]
    public static pb::MessageParser<CrxFileHeader> Parser { get { return _parser; } }

    [DebuggerNonUserCode]
    public static pbr::MessageDescriptor Descriptor
    {
        get { return TestReflection.Descriptor.MessageTypes[0]; }
    }

    [DebuggerNonUserCode]
    pbr::MessageDescriptor pb::IMessage.Descriptor
    {
        get { return Descriptor; }
    }

    [DebuggerNonUserCode]
    public CrxFileHeader()
    {
        OnConstruction();
    }

    partial void OnConstruction();

    [DebuggerNonUserCode]
    public CrxFileHeader(CrxFileHeader other) : this()
    {
        sha256WithRsa_ = other.sha256WithRsa_.Clone();
        sha256WithEcdsa_ = other.sha256WithEcdsa_.Clone();
        signedHeaderData_ = other.signedHeaderData_;
        _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
    }

    [DebuggerNonUserCode]
    public CrxFileHeader Clone()
    {
        return new CrxFileHeader(this);
    }

    /// <summary>Field number for the "sha256_with_rsa" field.</summary>
    public const int Sha256WithRsaFieldNumber = 2;
    private static readonly pb::FieldCodec<AsymmetricKeyProof> _repeated_sha256WithRsa_codec
        = pb::FieldCodec.ForMessage(18, AsymmetricKeyProof.Parser);
    private readonly pbc::RepeatedField<AsymmetricKeyProof> sha256WithRsa_ = new pbc::RepeatedField<AsymmetricKeyProof>();
    /// <summary>
    /// PSS signature with RSA public key. The public key is formatted as a
    /// X.509 SubjectPublicKeyInfo block, as in CRXâ‚‚. In the common case of a
    /// developer key proof, the first 128 bits of the SHA-256 hash of the 
    /// public key must equal the crx_id.
    /// </summary>
    [DebuggerNonUserCode]
    public pbc::RepeatedField<AsymmetricKeyProof> Sha256WithRsa
    {
        get { return sha256WithRsa_; }
    }

    /// <summary>Field number for the "sha256_with_ecdsa" field.</summary>
    public const int Sha256WithEcdsaFieldNumber = 3;
    private static readonly pb::FieldCodec<AsymmetricKeyProof> _repeated_sha256WithEcdsa_codec
        = pb::FieldCodec.ForMessage(26, AsymmetricKeyProof.Parser);
    private readonly pbc::RepeatedField<AsymmetricKeyProof> sha256WithEcdsa_ = new pbc::RepeatedField<AsymmetricKeyProof>();
    /// <summary>
    /// ECDSA signature, using the NIST P-256 curve. Public key appears in
    /// named-curve format.
    /// The pinned algorithm will be this, at least on 2017-01-01.
    /// </summary>
    [DebuggerNonUserCode]
    public pbc::RepeatedField<AsymmetricKeyProof> Sha256WithEcdsa
    {
        get { return sha256WithEcdsa_; }
    }

    /// <summary>Field number for the "signed_header_data" field.</summary>
    public const int SignedHeaderDataFieldNumber = 10000;
    private pb::ByteString signedHeaderData_ = pb::ByteString.Empty;
    /// <summary>
    /// The binary form of a SignedData message. We do not use a nested 
    /// SignedData message, as handlers of this message must verify the proofs
    /// on exactly these bytes, so it is convenient to parse in two steps.
    ///
    /// All proofs in this CrxFile message are on the value
    /// "CRX3 SignedData\x00" + signed_header_size + signed_header_data +
    /// archive, where "\x00" indicates an octet with value 0, "CRX3 SignedData"
    /// is encoded using UTF-8, signed_header_size is the size in octets of the
    /// contents of this field and is encoded using 4 octets in little-endian
    /// order, signed_header_data is exactly the content of this field, and
    /// archive is the remaining contents of the file following the header.
    /// </summary>
    [DebuggerNonUserCode]
    public pb::ByteString SignedHeaderData
    {
        get { return signedHeaderData_; }
        set
        {
            signedHeaderData_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
        }
    }

    [DebuggerNonUserCode]
    public override bool Equals(object other)
    {
        return Equals(other as CrxFileHeader);
    }

    [DebuggerNonUserCode]
    public bool Equals(CrxFileHeader other)
    {
        if (ReferenceEquals(other, null))
        {
            return false;
        }
        if (ReferenceEquals(other, this))
        {
            return true;
        }
        if (!sha256WithRsa_.Equals(other.sha256WithRsa_)) return false;
        if (!sha256WithEcdsa_.Equals(other.sha256WithEcdsa_)) return false;
        if (SignedHeaderData != other.SignedHeaderData) return false;
        return Equals(_unknownFields, other._unknownFields);
    }

    [DebuggerNonUserCode]
    public override int GetHashCode()
    {
        int hash = 1;
        hash ^= sha256WithRsa_.GetHashCode();
        hash ^= sha256WithEcdsa_.GetHashCode();
        if (SignedHeaderData.Length != 0) hash ^= SignedHeaderData.GetHashCode();
        if (_unknownFields != null)
        {
            hash ^= _unknownFields.GetHashCode();
        }
        return hash;
    }

    [DebuggerNonUserCode]
    public override string ToString()
    {
        return pb::JsonFormatter.ToDiagnosticString(this);
    }

    [DebuggerNonUserCode]
    public void WriteTo(pb::CodedOutputStream output)
    {
        sha256WithRsa_.WriteTo(output, _repeated_sha256WithRsa_codec);
        sha256WithEcdsa_.WriteTo(output, _repeated_sha256WithEcdsa_codec);
        if (SignedHeaderData.Length != 0)
        {
            output.WriteRawTag(130, 241, 4);
            output.WriteBytes(SignedHeaderData);
        }
        if (_unknownFields != null)
        {
            _unknownFields.WriteTo(output);
        }
    }

    [DebuggerNonUserCode]
    public int CalculateSize()
    {
        int size = 0;
        size += sha256WithRsa_.CalculateSize(_repeated_sha256WithRsa_codec);
        size += sha256WithEcdsa_.CalculateSize(_repeated_sha256WithEcdsa_codec);
        if (SignedHeaderData.Length != 0)
        {
            size += 3 + pb::CodedOutputStream.ComputeBytesSize(SignedHeaderData);
        }
        if (_unknownFields != null)
        {
            size += _unknownFields.CalculateSize();
        }
        return size;
    }

    [DebuggerNonUserCode]
    public void MergeFrom(CrxFileHeader other)
    {
        if (other == null)
        {
            return;
        }
        sha256WithRsa_.Add(other.sha256WithRsa_);
        sha256WithEcdsa_.Add(other.sha256WithEcdsa_);
        if (other.SignedHeaderData.Length != 0)
        {
            SignedHeaderData = other.SignedHeaderData;
        }
        _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
    }

    [DebuggerNonUserCode]
    public void MergeFrom(pb::CodedInputStream input)
    {
        uint tag;
        while ((tag = input.ReadTag()) != 0)
        {
            switch (tag)
            {
                default:
                    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
                    break;
                case 18:
                    {
                        sha256WithRsa_.AddEntriesFrom(input, _repeated_sha256WithRsa_codec);
                        break;
                    }
                case 26:
                    {
                        sha256WithEcdsa_.AddEntriesFrom(input, _repeated_sha256WithEcdsa_codec);
                        break;
                    }
                case 80002:
                    {
                        SignedHeaderData = input.ReadBytes();
                        break;
                    }
            }
        }
    }

}

public sealed partial class AsymmetricKeyProof : pb::IMessage<AsymmetricKeyProof>
{
    private static readonly pb::MessageParser<AsymmetricKeyProof> _parser = new pb::MessageParser<AsymmetricKeyProof>(() => new AsymmetricKeyProof());
    private pb::UnknownFieldSet _unknownFields;
    [DebuggerNonUserCode]
    public static pb::MessageParser<AsymmetricKeyProof> Parser { get { return _parser; } }

    [DebuggerNonUserCode]
    public static pbr::MessageDescriptor Descriptor
    {
        get { return TestReflection.Descriptor.MessageTypes[1]; }
    }

    [DebuggerNonUserCode]
    pbr::MessageDescriptor pb::IMessage.Descriptor
    {
        get { return Descriptor; }
    }

    [DebuggerNonUserCode]
    public AsymmetricKeyProof()
    {
        OnConstruction();
    }

    partial void OnConstruction();

    [DebuggerNonUserCode]
    public AsymmetricKeyProof(AsymmetricKeyProof other) : this()
    {
        publicKey_ = other.publicKey_;
        signature_ = other.signature_;
        _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
    }

    [DebuggerNonUserCode]
    public AsymmetricKeyProof Clone()
    {
        return new AsymmetricKeyProof(this);
    }

    /// <summary>Field number for the "public_key" field.</summary>
    public const int PublicKeyFieldNumber = 1;
    private pb::ByteString publicKey_ = pb::ByteString.Empty;
    [DebuggerNonUserCode]
    public pb::ByteString PublicKey
    {
        get { return publicKey_; }
        set
        {
            publicKey_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
        }
    }

    /// <summary>Field number for the "signature" field.</summary>
    public const int SignatureFieldNumber = 2;
    private pb::ByteString signature_ = pb::ByteString.Empty;
    [DebuggerNonUserCode]
    public pb::ByteString Signature
    {
        get { return signature_; }
        set
        {
            signature_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
        }
    }

    [DebuggerNonUserCode]
    public override bool Equals(object other)
    {
        return Equals(other as AsymmetricKeyProof);
    }

    [DebuggerNonUserCode]
    public bool Equals(AsymmetricKeyProof other)
    {
        if (ReferenceEquals(other, null))
        {
            return false;
        }
        if (ReferenceEquals(other, this))
        {
            return true;
        }
        if (PublicKey != other.PublicKey) return false;
        if (Signature != other.Signature) return false;
        return Equals(_unknownFields, other._unknownFields);
    }

    [DebuggerNonUserCode]
    public override int GetHashCode()
    {
        int hash = 1;
        if (PublicKey.Length != 0) hash ^= PublicKey.GetHashCode();
        if (Signature.Length != 0) hash ^= Signature.GetHashCode();
        if (_unknownFields != null)
        {
            hash ^= _unknownFields.GetHashCode();
        }
        return hash;
    }

    [DebuggerNonUserCode]
    public override string ToString()
    {
        return pb::JsonFormatter.ToDiagnosticString(this);
    }

    [DebuggerNonUserCode]
    public void WriteTo(pb::CodedOutputStream output)
    {
        if (PublicKey.Length != 0)
        {
            output.WriteRawTag(10);
            output.WriteBytes(PublicKey);
        }
        if (Signature.Length != 0)
        {
            output.WriteRawTag(18);
            output.WriteBytes(Signature);
        }
        if (_unknownFields != null)
        {
            _unknownFields.WriteTo(output);
        }
    }

    [DebuggerNonUserCode]
    public int CalculateSize()
    {
        int size = 0;
        if (PublicKey.Length != 0)
        {
            size += 1 + pb::CodedOutputStream.ComputeBytesSize(PublicKey);
        }
        if (Signature.Length != 0)
        {
            size += 1 + pb::CodedOutputStream.ComputeBytesSize(Signature);
        }
        if (_unknownFields != null)
        {
            size += _unknownFields.CalculateSize();
        }
        return size;
    }

    [DebuggerNonUserCode]
    public void MergeFrom(AsymmetricKeyProof other)
    {
        if (other == null)
        {
            return;
        }
        if (other.PublicKey.Length != 0)
        {
            PublicKey = other.PublicKey;
        }
        if (other.Signature.Length != 0)
        {
            Signature = other.Signature;
        }
        _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
    }

    [DebuggerNonUserCode]
    public void MergeFrom(pb::CodedInputStream input)
    {
        uint tag;
        while ((tag = input.ReadTag()) != 0)
        {
            switch (tag)
            {
                default:
                    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
                    break;
                case 10:
                    {
                        PublicKey = input.ReadBytes();
                        break;
                    }
                case 18:
                    {
                        Signature = input.ReadBytes();
                        break;
                    }
            }
        }
    }

}

public sealed partial class SignedData : pb::IMessage<SignedData>
{
    private static readonly pb::MessageParser<SignedData> _parser = new pb::MessageParser<SignedData>(() => new SignedData());
    private pb::UnknownFieldSet _unknownFields;
    [DebuggerNonUserCode]
    public static pb::MessageParser<SignedData> Parser { get { return _parser; } }

    [DebuggerNonUserCode]
    public static pbr::MessageDescriptor Descriptor
    {
        get { return TestReflection.Descriptor.MessageTypes[2]; }
    }

    [DebuggerNonUserCode]
    pbr::MessageDescriptor pb::IMessage.Descriptor
    {
        get { return Descriptor; }
    }

    [DebuggerNonUserCode]
    public SignedData()
    {
        OnConstruction();
    }

    partial void OnConstruction();

    [DebuggerNonUserCode]
    public SignedData(SignedData other) : this()
    {
        crxId_ = other.crxId_;
        _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
    }

    [DebuggerNonUserCode]
    public SignedData Clone()
    {
        return new SignedData(this);
    }

    /// <summary>Field number for the "crx_id" field.</summary>
    public const int CrxIdFieldNumber = 1;
    private pb::ByteString crxId_ = pb::ByteString.Empty;
    /// <summary>
    /// This is simple binary, not UTF-8 encoded mpdecimal; i.e. it is exactly
    /// 16 bytes long.
    /// </summary>
    [DebuggerNonUserCode]
    public pb::ByteString CrxId
    {
        get { return crxId_; }
        set
        {
            crxId_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
        }
    }

    [DebuggerNonUserCode]
    public override bool Equals(object other)
    {
        return Equals(other as SignedData);
    }

    [DebuggerNonUserCode]
    public bool Equals(SignedData other)
    {
        if (ReferenceEquals(other, null))
        {
            return false;
        }
        if (ReferenceEquals(other, this))
        {
            return true;
        }
        if (CrxId != other.CrxId) return false;
        return Equals(_unknownFields, other._unknownFields);
    }

    [DebuggerNonUserCode]
    public override int GetHashCode()
    {
        int hash = 1;
        if (CrxId.Length != 0) hash ^= CrxId.GetHashCode();
        if (_unknownFields != null)
        {
            hash ^= _unknownFields.GetHashCode();
        }
        return hash;
    }

    [DebuggerNonUserCode]
    public override string ToString()
    {
        return pb::JsonFormatter.ToDiagnosticString(this);
    }

    [DebuggerNonUserCode]
    public void WriteTo(pb::CodedOutputStream output)
    {
        if (CrxId.Length != 0)
        {
            output.WriteRawTag(10);
            output.WriteBytes(CrxId);
        }
        if (_unknownFields != null)
        {
            _unknownFields.WriteTo(output);
        }
    }

    [DebuggerNonUserCode]
    public int CalculateSize()
    {
        int size = 0;
        if (CrxId.Length != 0)
        {
            size += 1 + pb::CodedOutputStream.ComputeBytesSize(CrxId);
        }
        if (_unknownFields != null)
        {
            size += _unknownFields.CalculateSize();
        }
        return size;
    }

    [DebuggerNonUserCode]
    public void MergeFrom(SignedData other)
    {
        if (other == null)
        {
            return;
        }
        if (other.CrxId.Length != 0)
        {
            CrxId = other.CrxId;
        }
        _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
    }

    [DebuggerNonUserCode]
    public void MergeFrom(pb::CodedInputStream input)
    {
        uint tag;
        while ((tag = input.ReadTag()) != 0)
        {
            switch (tag)
            {
                default:
                    _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
                    break;
                case 10:
                    {
                        CrxId = input.ReadBytes();
                        break;
                    }
            }
        }
    }

}
#endregion
#endregion Designer generated code

你在这里有很多比我开始时,我这样做。使用此代码和注册表作家添加您的详细信息注册表,你可以有一个Chrome扩展部署/安装内部工具。

p1iqtdky

p1iqtdky3#

由于我们现在有2023,因此我们对CRX_REQUIRED_PROOF_MISSING也有不同的解决方案。
你要做的就是:
1.关闭Google Chrome
1.打开注册表(打开 * 运行 * 窗口[winkey+R]并键入:regedit,然后按Enter键)
1.转到HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallSources-如果某些部分不存在,则创建它。
1.在ExtensionInstallSources键/文件夹中创建新的字符串值,名称为:1 和值 *:我的<all_urls>天
1.运行Google Chrome,在地址栏中键入:chrome://policy/和点击回车-如果你做的一切都是正确的,你会看到你的新政策在 *Chrome的政策 * 部分
1.现在你可以安装你的扩展只拖放到浏览器上。
请注意,警告The extensions didn't come from the Chrome Web Store or were installed without your permission将添加到扩展名列表中的扩展名(chrome://extensions/)。
这就是全部。你不需要扩展名ID,也不需要私钥文件或其他任何东西。只需要扩展名 .crx文件。
`
-你不必允许所有的url作为你信任的安装源。你可以在official developer chrome documentation上找到允许的模式的完整列表。
来源:Chrome Enterprise - ExtensionInstallSource

相关问题