Reload limpa e recarrega todos os plugins do diretório de plugins.
()
| 114 | |
| 115 | // Reload limpa e recarrega todos os plugins do diretório de plugins. |
| 116 | func (m *Manager) Reload() { |
| 117 | m.mu.Lock() |
| 118 | defer m.mu.Unlock() |
| 119 | |
| 120 | m.plugins = make(map[string]Plugin) |
| 121 | |
| 122 | if err := os.MkdirAll(m.pluginsDir, 0o700); err != nil { |
| 123 | m.logger.Error("Não foi possível criar o diretório de plugins", zap.String("path", m.pluginsDir), zap.Error(err)) |
| 124 | return |
| 125 | } |
| 126 | |
| 127 | entries, err := os.ReadDir(m.pluginsDir) |
| 128 | if err != nil { |
| 129 | m.logger.Error("Erro ao ler o diretório de plugins", zap.Error(err)) |
| 130 | return |
| 131 | } |
| 132 | |
| 133 | // Security (C3/H9): Verify plugin signatures before loading |
| 134 | verifier := NewPluginVerifier() |
| 135 | if verifier.AllowsUnsigned() { |
| 136 | m.logger.Warn("SECURITY: unsigned plugins are allowed (CHATCLI_ALLOW_UNSIGNED_PLUGINS=true) — disable in production") |
| 137 | } |
| 138 | |
| 139 | for _, entry := range entries { |
| 140 | if strings.HasSuffix(entry.Name(), ".sig") || strings.HasPrefix(entry.Name(), ".") { |
| 141 | continue // skip signature files and hidden files |
| 142 | } |
| 143 | pluginPath := filepath.Join(m.pluginsDir, entry.Name()) |
| 144 | |
| 145 | // Verify plugin signature |
| 146 | if err := verifier.VerifyPlugin(pluginPath); err != nil { |
| 147 | if errors.Is(err, ErrNoSignature) && verifier.AllowsUnsigned() { |
| 148 | m.logger.Warn("Loading unsigned plugin (dev mode)", zap.String("plugin", entry.Name())) |
| 149 | } else { |
| 150 | m.logger.Warn("Plugin signature verification failed, skipping", |
| 151 | zap.String("plugin", entry.Name()), zap.Error(err)) |
| 152 | continue |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | plugin, err := NewPluginFromPath(pluginPath) |
| 157 | if err != nil { |
| 158 | m.logger.Warn("Arquivo inválido no diretório de plugins, pulando.", zap.String("path", pluginPath), zap.Error(err)) |
| 159 | continue |
| 160 | } |
| 161 | m.plugins[plugin.Name()] = plugin |
| 162 | } |
| 163 | |
| 164 | // Re-inject builtins that were not overridden by external plugins |
| 165 | for name, bp := range m.builtins { |
| 166 | if _, exists := m.plugins[name]; !exists { |
| 167 | m.plugins[name] = bp |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | m.logger.Info("Plugins recarregados.", zap.Int("count", len(m.plugins))) |
| 172 | } |
| 173 |
no test coverage detected